Expo (React Native)

Pathrule3 Rules • 3 Memories • 1 Skill

A guardrail bundle for modern Expo (SDK 56+) React Native projects on the New Architecture. It keeps Expo Router navigation typed and predictable, treats the app config as the single source of native truth via Continuous Native Generation, and prevents the classic OTA crash where a JavaScript update references native code that isn't in the installed binary. Built for teams shipping with EAS Build and EAS Update.

Suggested path map

Pathrule places each piece on the matching path, so your assistant only sees it where it belongs. This is the scoping you get on import; you can adjust it in your workspace.

/ workspace root
Never hand-edit ios/ or android/ when CNG is enabled
Bump runtimeVersion before any OTA update that touches native code
SDK 56 and New Architecture: what is on by default and what changed
EAS Build and Update channel discipline
expo-review
app/
Navigate with Expo Router typed routes; do not import from react-navigation directly
Expo Router file conventions and layout nesting

Rules

3
Never hand-edit ios/ or android/ when CNG is enabled/roothighstrictios/ and android/ are generated by expo prebuild and wiped on the next run; native config belongs in app.config.ts and config plugins only.
1Expo's Continuous Native Generation (CNG) regenerates the `ios/` and `android/` directories from `app.config.ts` (or `app.json`) and Expo config plugins each time `expo prebuild` runs. Any manual edit to those directories will be silently overwritten on the next prebuild.
2 
3- Add native dependencies that require code changes via an Expo config plugin, not by editing `MainApplication.kt`, `AppDelegate.swift`, `Podfile`, or any native source file directly.
4- If a library lacks a config plugin, write one (`withDangerousMod` / `withInfoPlist` / `withAndroidManifest`) rather than touching generated files.
5- Commit `ios/` and `android/` only when you have abandoned CNG for that project (a deliberate "bare workflow" decision). A team using EAS Build on a managed or CNG project should add both directories to `.gitignore`.
6- Track native dependency intent in `app.config.ts` plugins and in `package.json`; that is the reproducible source of truth, not the generated directory contents.
Bump runtimeVersion before any OTA update that touches native code/roothighstrictAn EAS Update whose JavaScript references a native module not present in the installed binary will crash the app silently on launch.
1EAS Update delivers JavaScript-only over-the-air. The `runtimeVersion` field in `app.config.ts` is the contract between a binary and the JavaScript it can run. If you push an OTA update that imports a native module (or changes a module's API) that is absent in already-installed binaries, those binaries will crash on launch with no user-visible error.
2 
3- Set `runtimeVersion: { policy: 'appVersion' }` in `app.config.ts` so the runtime version tracks the app version string and bumping the app version automatically gates the update to matching binaries. This is the safest default for most teams.
4- Alternatively use `fingerprintRuntimeVersion` (Expo's hash-based policy) to derive the runtime version from a reproducible fingerprint of the native layer. Run `npx expo-updates fingerprint:generate` to inspect what the hash covers.
5- When you add or upgrade a native module that requires a new build, always ship a new EAS Build before or alongside the OTA update. Never push the OTA update first.
6- Keep `eas.json` update channels aligned with build profiles: development updates go to `development`, preview to `preview`, production to `production`. Do not push production JavaScript to a channel whose binaries still target an older runtime version.

Memories

3
SDK 56 and New Architecture: what is on by default and what changed/rootNew Architecture (Fabric + JSI) is default in SDK 56; bridgeless mode is on; old bridge libraries that use NativeModules.X no longer work.
1Expo SDK 56 (2025) ships with the New Architecture enabled and bridgeless mode on by default. This changes the runtime assumptions that older libraries make.
2 
3- Libraries that access `NativeModules.SomeModule` directly (the old bridge API) no longer work in bridgeless mode. Replace them with TurboModule-compatible alternatives or open an issue upstream.
4- JSI-based native code is synchronous; avoid blocking the JS thread with heavy synchronous calls in JSI modules.
5- `useAnimatedRef`, `measure`, and Reanimated's worklets run on the UI thread via JSI. Do not access React state or closures from a worklet without using `useSharedValue` or `runOnJS`.
6- Check third-party library compatibility at https://reactnative.directory and filter by New Architecture support before adding dependencies. Libraries marked "New Architecture: supported" use the interop layer only; the full native module must be TurboModule-compatible for optimal performance.
7- `expo-modules-core` >= 1.12 (bundled with SDK 56) provides the Swift/Kotlin Expo Modules API that generates a TurboModule automatically; prefer it when writing custom native code over manual TurboModule boilerplate.
Expo Router file conventions and layout nesting/appScreens, groups, layouts, and the special +not-found and +html files that the file-based router recognizes.
1Expo Router uses the `app/` directory for all screens and layouts. The filename maps directly to the URL and navigation stack.
2 
3- `_layout.tsx` defines the navigator for its directory level. Every directory that has more than one screen should have a `_layout.tsx`; without one, Expo Router inserts an implicit Stack.
4- `index.tsx` is the index screen for its directory. A file named `(tab).tsx` renders as a tab route, not an extra folder segment, because of the group syntax.
5- Use `(groupName)/` directories to group related screens under a shared layout without adding a URL segment. Use `[param]` for dynamic segments and `[...rest]` for catch-all segments.
6- `+not-found.tsx` at the root of `app/` renders when no route matches a deep link or path.
7- `+html.tsx` customises the root HTML shell for web targets only; it does not affect native.
8- `app/_layout.tsx` (the root layout) is the right place for providers (theme, auth context, i18n) that wrap the whole app. Keep the root layout minimal and defer slow providers behind a loading splash so the first frame paints quickly.
9- Static routes in `app/` are discovered at build time for typed routes; the codegen runs automatically when you run `expo start` or `expo export`.
EAS Build and Update channel discipline/rootBuild profiles, update channels, and runtime version policies must stay aligned so OTA updates reach the right binaries.
1EAS Build and EAS Update together form the CI/CD pipeline for Expo apps. Their channel and runtime-version contracts must be kept consistent to avoid delivering incompatible JavaScript to installed binaries.
2 
3- `eas.json` defines `build` profiles (`development`, `preview`, `production`) and links each to an update channel with `channel: 'production'`. Updates are only delivered to binaries built with the same channel.
4- The `development` profile should set `developmentClient: true` and build an `expo-dev-client` binary. This build runs local or hosted dev bundles and should never receive production OTA updates.
5- Run `eas build --profile preview` for QA binaries; push OTA updates to the `preview` channel for that audience, and to `production` only after QA approval.
6- Use `eas update --branch <name> --channel <channel>` to publish; always specify the channel explicitly. The `main` branch of a channel receives the update; create named branches (e.g. `rollout-1.2.0`) to stage rollouts before promoting.
7- After publishing, check `eas update:list` to confirm the runtime version on the published update matches the runtime version on the target binaries. A mismatch means the update will be silently skipped by those binaries.

Skills

1
expo-review/rootPre-merge checklist for Expo SDK 56 apps: CNG discipline, OTA safety, Expo Router conventions, New Architecture compatibility, and EAS channel alignment.
1---
2name: expo-review
3description: Review checklist for Expo (SDK 56+) React Native apps on the New Architecture. Use before merging changes that touch native dependencies, app.config.ts, EAS config, app/ routes, or OTA update workflows.
4---
5 
6# Expo review
7 
8## Native config and CNG
9- [ ] No hand edits to `ios/` or `android/` when CNG is in use; native config changes go through `app.config.ts` and config plugins.
10- [ ] New native dependencies have an Expo config plugin, or a custom one has been written rather than editing generated files.
11- [ ] `ios/` and `android/` are gitignored (or CNG has been explicitly abandoned).
12 
13## OTA safety
14- [ ] If any native module was added, removed, or upgraded, a new EAS Build precedes or accompanies the OTA update.
15- [ ] `runtimeVersion` policy is set (`appVersion` or `fingerprintRuntimeVersion`); it has not been hard-coded to a stale value.
16- [ ] The update channel in `eas.json` aligns with the build profile (development/preview/production); no production JS is targeted at a development channel.
17 
18## Expo Router
19- [ ] Navigation uses `Link`, `router.push/replace/navigate` from `expo-router`, not `useNavigation().navigate()`.
20- [ ] Route params are read with `useLocalSearchParams` from `expo-router`, not `useRoute().params`.
21- [ ] `typedRoutes: true` is set and `href` strings are valid route paths.
22- [ ] Each directory with multiple screens has a `_layout.tsx`; group directories `(name)/` are used instead of empty path segments.
23- [ ] Providers that wrap the whole app are in the root `app/_layout.tsx`, not duplicated across screens.
24 
25## New Architecture compatibility
26- [ ] No third-party library uses `NativeModules.X` (old bridge API) without a verified bridgeless-compatible replacement.
27- [ ] Reanimated worklets do not access React state directly; `useSharedValue` and `runOnJS` are used across the thread boundary.
28- [ ] New native modules use `expo-modules-core` (Swift/Kotlin Expo Modules API), not manual TurboModule boilerplate.
29 
30## EAS discipline
31- [ ] `eas build` and `eas update` channel/profile assignments are consistent in `eas.json`.
32- [ ] After publishing, `eas update:list` confirms the runtime version matches the target binaries.

Why this pattern

AI agents edit native files directly and push OTA updates that reference native modules missing from the installed binary, crashing production apps.

Built for Teams building cross-platform Expo apps with EAS Build and EAS Update..

Keeps your assistant from:

  • Hand-editing ios/ and android/ folders that prebuild regenerates and wipes
  • Pushing an EAS Update with native changes without bumping runtimeVersion, crashing live users
  • Importing from react-navigation directly and bypassing Expo Router typed routes
License
Apache-2.0
Version
1.0.0
Updated
2026-06-09
View source