Expo Development Patterns
Your EAS build is green, the app runs in Expo Go on your phone, and then a teammate adds react-native-vision-camera and Expo Go silently stops loading the app. Half your prompts assume the managed workflow, the other half assume a development build, and the AI keeps generating code for whichever one it guessed. The fix is not “use AI more” - it is giving the AI the two facts that actually constrain an Expo project: which SDK you are on, and whether you are running Expo Go or a dev build.
This recipe shows the Expo workflows that survive contact with EAS, app store review, and SDK upgrades, driven through Cursor, Claude Code, and Codex.
What You’ll Walk Away With
Section titled “What You’ll Walk Away With”- A scaffolding prompt that pins the Expo SDK and the workflow type so the AI stops guessing
- A copy-paste prompt for an auth-protected Expo Router tab layout that handles the redirect race
- A NativeWind setup prompt that produces a typed theme, not just
classNamestrings - An EAS Build + OTA-update prompt wired into GitHub Actions via the GitHub MCP server
- A “When This Breaks” map of the failure modes that actually cost you a day: SDK upgrades, Expo Go incompatibility, EAS credentials, and OTA runtime mismatches
Scaffolding an Expo App
Section titled “Scaffolding an Expo App”The single biggest lever on output quality is naming the SDK version and the workflow type up front. As of June 2026 the current line is Expo SDK 56 (React Native 0.85, React 19.2, Hermes as the default engine). Pin it.
Open Cursor in an empty folder, enter Agent mode (Cmd/Ctrl + I), and paste:
Scaffold a new Expo SDK 56 app using
npx create-expo-app@latestwith the TypeScript template. Add Expo Router (file-based routing), NativeWind v4 for styling, and configure a development build (not Expo Go) because we will add native modules later. Set upeas.jsonwithdevelopment,preview, andproductionprofiles.
Agent mode runs the CLI, edits app.json/eas.json, and shows a diff before applying. Review the eas.json profiles before accepting.
From an empty directory:
claude "Scaffold an Expo SDK 56 app with create-expo-app (TypeScript template), \Expo Router, NativeWind v4, and an eas.json with development/preview/production \profiles. We need a development build, not Expo Go."Claude Code runs the scaffolder, then iterates on config files in the same session. Use a follow-up like claude "now add a typed env config with expo-constants and zod validation" to keep the context.
In the Codex CLI, prompts are positional. From an empty directory:
codex "Scaffold an Expo SDK 56 app with create-expo-app (TypeScript template), \Expo Router, and NativeWind v4. Configure eas.json with development, preview, \and production profiles for a development build."For a long-running EAS setup you do not want to babysit, use codex --full-auto (workspace-write sandbox, on-request approvals) or hand the task to Codex Cloud so it runs the scaffold and opens a PR.
Expo Router: Auth-Protected Tabs
Section titled “Expo Router: Auth-Protected Tabs”File-based routing is where most Expo time goes, and the auth-gate redirect is where most bugs live: redirect before the layout mounts and you get a “navigate before mounting the Root Layout” warning; redirect in render and you flash protected content for a frame.
The AI should produce a tree like this and the matching layout files:
app/ _layout.tsx // SessionProvider + Stack.Protected guard (auth)/ _layout.tsx login.tsx (tabs)/ _layout.tsx // Tabs index.tsx profile.tsxHow to evaluate the output: confirm it keeps the splash screen up with SplashScreen.preventAutoHideAsync() until SecureStore resolves, and that the gate uses Stack.Protected (SDK 53+) rather than a useEffect + router.replace, which causes the redirect flash. If you see router.replace inside a render path, send it back with “move the guard out of render.”
Styling with NativeWind
Section titled “Styling with NativeWind”The trap with NativeWind prompts is getting a wall of className="bg-blue-500" with magic values. Ask for a typed theme so colors come from one place.
Reject the first draft if the button hard-codes hex values instead of bg-primary - the whole point is that a rebrand is a one-file change.
EAS Build, OTA Updates, and CI
Section titled “EAS Build, OTA Updates, and CI”This is where the GitHub MCP server earns its place. Without it, the AI writes a plausible-looking eas-build.yml, you paste it, push, and discover the secret names are wrong. With the GitHub MCP server connected, the agent reads your actual repo - existing workflows, secret names, branch protection - and writes CI that matches.
Setup is identical across all three tools (it is the official remote GitHub MCP server):
Add to .cursor/mcp.json:
{ "mcpServers": { "github": { "url": "https://api.githubcopilot.com/mcp/" } }}claude mcp add --transport http github https://api.githubcopilot.com/mcp/Authenticate with /mcp inside the REPL on first use.
Add to ~/.codex/config.toml:
[mcp_servers.github]url = "https://api.githubcopilot.com/mcp/"Push Notifications
Section titled “Push Notifications”Expo notifications have one production gotcha the AI routinely skips: you must send the push token to your backend and handle the notification-tap deep link, not just request permission.
When This Breaks
Section titled “When This Breaks”Mobile is where AI confidence and reality diverge the most. The four failure modes below are the ones that actually cost a day:
- SDK upgrade breakage.
npx expo install expo@^56is the easy part; transitive native deps and config plugins are not. Runnpx expo-doctorand paste its full output into the AI with: “We just upgraded to SDK 56. Here is expo-doctor output. Fix each version mismatch usingnpx expo install --fix, and flag any third-party package with no SDK 56 support.” Do not let the AI hand-edit versions inpackage.json-expo installresolves the compatible ranges. - “It works in Expo Go but not the dev build” (or vice-versa). This is almost always a module that is not in Expo Go, or a config plugin that only applies during
prebuild. Tell the AI which environment fails and ask it to check whether the module requires a development build and a config plugin entry inapp.json. - EAS credentials and provisioning. When
eas buildfails on signing, paste the build log. The usual culprits are an expired Apple distribution cert or a missing push key. Ask the AI to interpret the specific EAS error rather than regenerating credentials blindly -eas credentialsis interactive and destructive if misused. - OTA runtime mismatch. If an
eas update“succeeds” but devices never pick it up, theruntimeVersionof the update does not match any installed build. Have the AI diff the update’s runtime version against your latest store build before assuming the update pipeline is broken.
What’s Next
Section titled “What’s Next”- React Native patterns for the bare workflow and native module details
- Flutter Development Patterns if you are comparing cross-platform stacks
- Backend integration for the API your device-token and sync endpoints talk to