Best Practices
General tips & best practices to follow when developing your app
Adding New Packages
When installing new Expo packages (packages from the Expo SDK, such as expo-image-picker, or expo-sensors, etc.), make sure to use npx expo install <your-package> within apps/expo instead of the basic pnpm add <your-package> -F <workspace...>.
The reason is because the expo install comment ensures it fetches the version of the package that is most compatible with your current version of Expo.
You can install non-Expo packages (e.g. react-redux, jotai, etc.) like normal (pnpm add ... -F <workspace...>).
If you are installing Expo packages and would also like to use it in the NextJS web app, you MUST add the package to next.config.mjs's transpilePackages array.
For example, if you added expo-haptics, and also want to use it in NextJS, update next.config.mjs as so:
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
transpilePackages: [
"react-native",
"react-native-web",
"expo",
"expo-haptics", // <= ADDED!
]Documentation: https://docs.expo.dev/guides/using-nextjs/
Bottom Sheets
We use gorhom's React Native Bottom Sheet the most popular, feature-complete, actively maintained library. It also supports web and the New Architecture.
However, there are a few downsides. It does not support Nativewind out-of-the-box (leading to broken light/dark theme toggling), and setting it up correctly can be tricky.
We've addressed these downsides, so when you want to use the Bottom Sheet, you should import from the packages/design/components/bottom-sheet package
Example (see packages/app/features/design-showcase/components/navigation-showcase):
// IMPORT FROM THE LOCAL PACKAGE
import {
BottomSheetModal,
BottomSheetView,
type BottomSheetRef,
} from "@repo/design/components/bottom-sheet";
// USE LIKE NORMAL
export const YourComponent = () => {
const bottomSheetRef = useRef<BottomSheetRef>(null);
return (
<View className="gap-4">
<BottomSheetModal
ref={bottomSheetRef}
snapPoints={["50%", "90%"]}
>
<BottomSheetView className="flex-1 bg-card items-center justify-center h-64">
<P>Awesomedd 🎉</P>
</BottomSheetView>
</BottomSheetModal>
...
)
}The local package re-maps the style props to make the component styling work with Nativewind, and adds some default styles to support light/dark mode.
Analyzing JavaScript Bundle
We always want to ship the smallest bundle we can. This is important for startup time, performance, app size, etc. The biggest culprit of bundle size bloat are third party libraries.
Fortuantely, Expo provides a tool called Atlas which makes inspecting the JavaScript bundle very easy: https://docs.expo.dev/guides/analyzing-bundles/
Expo Config File app.config.ts
Whenever you make configuration changes via this file, you will need to re-build your application.
React Compiler
Although Expo's implementation of React Compiler is experimental, we believe the performance benefits are significant and is enabled by default
In practice, this means that the Expo bundler automatically handles memoization and other performance optimizations when bundling your code. You can remove instances of useCallback, useMemo, and React.memo in favor of the automatic memoization.
If you run into an issues with it, you can disable it by setting the value in app.config.ts to false:
"experiments": {
"reactCompiler": false
}You can learn more about Expo + React Compiler here: https://docs.expo.dev/guides/react-compiler/
Watch For Tailwind Components
Tailwind is constantly watching for changes to files in order to process their styles. In order to keep this performant, we need to specify the directories where our UI files with classNames are.
This configuration is in apps/expo/tailwind.config.js's content section. By default, we watch over our UI packages, app package, and Expo app.
If you decide to create new components that use Tailwind classNames, and thus need Tailwind to process the styles, be sure to update tailwind.config.js with the new directory.