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 .env files with EXPO_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/FolderPurpose
(src/)app/_layout.tsxRoot layout (wraps all routes)
(src/)app/index.tsxHome screen (/)
(src/)app/about.tsx/about route
(src/)app/[id].tsxDynamic route (/123)
(src/)app/[...rest].tsxCatch-all route
(src/)app/(tabs)/_layout.tsxTab group layout
(src/)app/(tabs)/index.tsxFirst tab
(src/)app/(auth)/login.tsxAuth group route (guarded group in SDK 55)
(src/)app/+not-found.tsx404 page
(src/)app/+html.tsxCustom HTML wrapper (web)
(src/)app/modal.tsxModal 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:

  1. Don't import from react-native what belongs in expo-* — e.g., Camera is from expo-camera, not react-native
  2. Don't use react-native-cli commands — Expo projects use npx expo or eas
  3. Don't assume bare workflow — most Expo projects use managed workflow now; Expo Modules API bridges the gap
  4. Don't confuse React Navigation API with Expo Router — Expo Router wraps React Navigation but has its own file-based API
  5. Don't invent config plugin names — if unsure, check https://docs.expo.dev/config-plugins/introduction/
  6. Expo Router API routesapp/api/route+api.ts pattern exists for server endpoints but is relatively new. Verify usage patterns against docs.
  7. StatusBar — use expo-status-bar (not react-native StatusBar) in Expo projects
  8. Permissions — each expo-* package handles its own permissions via requestPermissionsAsync(). Don't use a generic permissions library.
  9. expo-av is DEPRECATED since SDK 55 — use expo-video and expo-audio instead. Do NOT import from expo-av.
  10. newArchEnabled is GONE — do NOT add newArchEnabled to app.json in SDK 55+. The New Architecture is mandatory.
  11. 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.
  12. Package versioning changed — since SDK 55, all expo-* packages use the SDK major version. e.g., expo-camera@^55.0.0 not expo-camera@^15.0.0.
  13. expo-blur API changed on Android — SDK 55 requires <BlurTargetView> wrapper for background content on Android. Old API still works on iOS.
  14. @expo/ui exists now — native SwiftUI/Compose components (DatePicker, Toggle, ProgressView, etc.). Don't confuse with regular RN components.
  15. 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:

  1. Tell the user you're not certain about the exact API
  2. Point them to the specific docs page: https://docs.expo.dev/versions/latest/sdk/<package-name>/
  3. Never invent props, methods, or import paths