expo-reference
Authoritative Expo & React Native reference for building mobile apps with Expo SDK. Use this skill whenever the user asks to build, scaffold, debug, or modify a React Native or Expo project — including component usage, navigation setup, native module configuration, app.json/app.config.js settings, EAS Build/Submit, or Expo Router. Also trigger when the user mentions Expo SDK, expo-router, expo-camera, expo-notifications, or any expo-* package. This skill prevents hallucination by providing verified API surfaces and directing you to the docs when uncertain.
Expo Reference Skill
Reference version: Expo SDK 55 (released February 25, 2026), React Native 0.83, React 19.2.0 Docs source: https://docs.expo.dev/ — verified April 2026
This skill provides a curated, verified reference of the Expo ecosystem. It is designed to prevent hallucination by giving you concrete API surfaces to work from.
Golden Rule
If a component, prop, or API is not listed in this skill's references, do NOT assume it exists. Instead, tell the user: "I'm not 100% sure this API exists in the current Expo SDK. Check https://docs.expo.dev/ to verify." This is far better than confidently writing code that imports non-existent modules.
When to Use This Skill
- User wants to create or modify a React Native / Expo app
- User asks about Expo components, APIs, or configuration
- User needs help with Expo Router, EAS, or native modules
- User is debugging Expo-specific issues
- User mentions any
expo-*package
Quick Architecture Overview
Expo apps in 2026 use:
- Expo SDK 55 (based on React Native 0.83, React 19.2.0 — New Architecture is mandatory, Legacy Architecture removed)
- Expo Router v4+ for file-based routing (built on React Navigation) — new features: SplitView, guarded groups, synchronous layouts
- EAS (Expo Application Services) for builds, updates, and submissions
- Expo Modules API for custom native modules (replaces old bare workflow patterns)
- expo-dev-client for custom development builds
- @expo/ui for native SwiftUI / Jetpack Compose components (DatePicker, Toggle, ProgressView, etc.)
- expo-widgets (alpha) for iOS Home Screen Widgets and Live Activities
- New versioning: all expo-* packages now share the SDK major version (e.g., expo-camera@^55.0.0)
Project Setup
# Create new project (always use this, not react-native init)
npx create-expo-app@latest my-app
npx create-expo-app@latest my-app --template default@sdk-55 # explicit SDK 55
# Start development
npx expo start # start dev server
npx expo start --clear # clear cache and start
SDK 55 default template changes: App code now lives in /src/app instead of /app. The template uses Native Tabs API for platform-native tab experience.
Configuration
app.json / app.config.js
Key fields (not exhaustive — check docs for full list):
{
"expo": {
"name": "MyApp",
"slug": "my-app",
"version": "1.0.0",
"sdkVersion": "55.0.0",
"orientation": "portrait", // portrait | landscape | default
"icon": "./assets/icon.png", // 1024x1024 PNG, no transparency for iOS
"splash": { // or use "splash.image" shorthand
"image": "./assets/splash.png",
"resizeMode": "contain", // contain | cover
"backgroundColor": "#ffffff"
},
"scheme": "myapp", // deep linking scheme
// NOTE: "newArchEnabled" was REMOVED in SDK 55 — New Architecture is mandatory
"ios": {
"bundleIdentifier": "com.example.myapp",
"supportsTablet": true
},
"android": {
"package": "com.example.myapp",
"adaptiveIcon": {
"foregroundImage": "./assets/adaptive-icon.png",
"backgroundColor": "#ffffff"
}
},
"plugins": [], // Expo config plugins
"extra": { // Custom values accessible via expo-constants
"eas": { "projectId": "..." }
}
}
}
Environment Variables
- Use
.envfiles withEXPO_PUBLIC_prefix for client-side vars - Access via
process.env.EXPO_PUBLIC_MY_VAR - Server-side env vars (no prefix) are only available in EAS Build or API routes
Expo Router (File-Based Routing)
The routing system lives in the app/ directory (or src/app/ in SDK 55 default template).
SDK 55 new features: SplitView support (tablet-optimized two-pane layouts), guarded groups (auth guards at folder level), synchronous layouts (no more flickers during tab transitions), early RSC (React Server Components) groundwork.
File Conventions
| File/Folder | Purpose |
|---|---|
(src/)app/_layout.tsx | Root layout (wraps all routes) |
(src/)app/index.tsx | Home screen (/) |
(src/)app/about.tsx | /about route |
(src/)app/[id].tsx | Dynamic route (/123) |
(src/)app/[...rest].tsx | Catch-all route |
(src/)app/(tabs)/_layout.tsx | Tab group layout |
(src/)app/(tabs)/index.tsx | First tab |
(src/)app/(auth)/login.tsx | Auth group route (guarded group in SDK 55) |
(src/)app/+not-found.tsx | 404 page |
(src/)app/+html.tsx | Custom HTML wrapper (web) |
(src/)app/modal.tsx | Modal route (use presentation: 'modal' in layout) |
Navigation API
import { Link, useRouter, useLocalSearchParams, useGlobalSearchParams, Stack, Tabs } from 'expo-router';
// Declarative
<Link href="/profile/123">Go to profile</Link>
<Link href={{ pathname: '/profile/[id]', params: { id: '123' } }}>Profile</Link>
// Imperative
const router = useRouter();
router.push('/profile/123');
router.replace('/login');
router.back();
router.canGoBack();
router.dismiss(); // dismiss modal
router.dismissAll(); // dismiss all modals
// Params
const { id } = useLocalSearchParams(); // from current route only
const params = useGlobalSearchParams(); // from entire URL
Layout Components
// Stack layout
import { Stack } from 'expo-router';
<Stack screenOptions={{ headerShown: false }}>
<Stack.Screen name="index" options={{ title: 'Home' }} />
<Stack.Screen name="modal" options={{ presentation: 'modal' }} />
</Stack>
// Tab layout
import { Tabs } from 'expo-router';
<Tabs screenOptions={{ tabBarActiveTintColor: '#007AFF' }}>
<Tabs.Screen name="index" options={{ title: 'Home', tabBarIcon: ({ color }) => <Icon name="home" color={color} /> }} />
<Tabs.Screen name="settings" options={{ title: 'Settings' }} />
</Tabs>
Core Components Reference
Read references/components.md for the full list of React Native core components and their key props.
Expo SDK Packages Reference
Read references/expo-packages.md for the full list of verified expo-* packages, their imports, and core APIs.
Common Patterns
Safe Area Handling
import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context';
// Wrap app root in SafeAreaProvider
// Use SafeAreaView or useSafeAreaInsets() hook in screens
Fonts
import { useFonts } from 'expo-font';
import * as SplashScreen from 'expo-splash-screen';
SplashScreen.preventAutoHideAsync();
export default function App() {
const [loaded] = useFonts({
'Inter-Bold': require('./assets/fonts/Inter-Bold.ttf'),
});
useEffect(() => {
if (loaded) SplashScreen.hideAsync();
}, [loaded]);
if (!loaded) return null;
// ...
}
Supabase Integration (relevant for NeuroPlan-type stacks)
// lib/supabase.ts
import { createClient } from '@supabase/supabase-js';
import AsyncStorage from '@react-native-async-storage/async-storage';
export const supabase = createClient(
process.env.EXPO_PUBLIC_SUPABASE_URL!,
process.env.EXPO_PUBLIC_SUPABASE_ANON_KEY!,
{
auth: {
storage: AsyncStorage,
autoRefreshToken: true,
persistSession: true,
detectSessionInUrl: false, // important for React Native
},
}
);
EAS Build & Submit
# Install EAS CLI
npm install -g eas-cli
# Configure
eas build:configure
# Build
eas build --platform ios # iOS build
eas build --platform android # Android build
eas build --platform all # both
# Submit to stores
eas submit --platform ios
eas submit --platform android
# OTA updates
eas update --branch production --message "Bug fix"
eas.json structure
{
"cli": { "version": ">= 5.0.0" },
"build": {
"development": {
"developmentClient": true,
"distribution": "internal"
},
"preview": {
"distribution": "internal"
},
"production": {}
},
"submit": {
"production": {}
}
}
Things That Often Cause Hallucination
These are areas where Claude frequently makes mistakes. Be extra careful:
- Don't import from
react-nativewhat belongs inexpo-*— e.g.,Camerais fromexpo-camera, notreact-native - Don't use
react-native-clicommands — Expo projects usenpx expooreas - Don't assume bare workflow — most Expo projects use managed workflow now; Expo Modules API bridges the gap
- Don't confuse React Navigation API with Expo Router — Expo Router wraps React Navigation but has its own file-based API
- Don't invent config plugin names — if unsure, check https://docs.expo.dev/config-plugins/introduction/
- Expo Router API routes —
app/api/route+api.tspattern exists for server endpoints but is relatively new. Verify usage patterns against docs. - StatusBar — use
expo-status-bar(notreact-nativeStatusBar) in Expo projects - Permissions — each expo-* package handles its own permissions via
requestPermissionsAsync(). Don't use a generic permissions library. - expo-av is DEPRECATED since SDK 55 — use
expo-videoandexpo-audioinstead. Do NOT import fromexpo-av. - newArchEnabled is GONE — do NOT add
newArchEnabledto app.json in SDK 55+. The New Architecture is mandatory. - Template path changed — SDK 55 default template puts app code in
/src/app, not/app. Both work, but don't assume one or the other without checking. - Package versioning changed — since SDK 55, all expo-* packages use the SDK major version. e.g.,
expo-camera@^55.0.0notexpo-camera@^15.0.0. - expo-blur API changed on Android — SDK 55 requires
<BlurTargetView>wrapper for background content on Android. Old API still works on iOS. - @expo/ui exists now — native SwiftUI/Compose components (DatePicker, Toggle, ProgressView, etc.). Don't confuse with regular RN components.
- Xcode 26 required — SDK 55 requires Xcode 26 for iOS builds.
When You're Unsure
If you need to reference an API that's not in this skill:
- Tell the user you're not certain about the exact API
- Point them to the specific docs page:
https://docs.expo.dev/versions/latest/sdk/<package-name>/ - Never invent props, methods, or import paths