Expo SDK Basics
10 examples to get you started with Expo SDK packages - 7 basic and 3 intermediate. Covers installing with npx expo install, SDK versioning, and how native modules boot inside your app.
Search across all documentation pages
10 examples to get you started with Expo SDK packages - 7 basic and 3 intermediate. Covers installing with npx expo install, SDK versioning, and how native modules boot inside your app.
Scaffold an Expo app with SDK 57 before adding SDK packages:
npx create-expo-app@latest SdkBasics --template blank-typescript
cd SdkBasicsConfirm the SDK pin:
{
"dependencies": {
"expo": "~57.0.4",
"react": "19.2.3",
"react-native": "0.86.0"
}
}Tooling: These examples target Expo SDK 57 (
expo~57.0.4), React Native 0.86.0, and React 19.2.3.
Expo SDK packages are native modules with a JavaScript API - camera, filesystem, notifications, and dozens more ship as npm packages versioned against a single expo release.
import { StatusBar } from "expo-status-bar";
import { StyleSheet, Text, View } from "react-native";
export default function App() {
return (
<View style={styles.container}>
<Text style={styles.title}>Expo SDK packages</Text>
<Text style={styles.body}>
JavaScript imports call into native code compiled for SDK 57 - not
browser polyfills.
</Text>
<StatusBar style="auto" />
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: "center", padding: 24, gap: 8 },
title: { fontSize: 22, fontWeight: "700" },
body: { fontSize: 15, color: "#4b5563", lineHeight: 22 },
});expo-camera, expo-location, …) is an Expo module with iOS and Android implementationsexpo package is the SDK root - its version defines which module versions are compatibleRelated: ../expo-platform/expo-platform-basics/expo-platform-basics.md - platform layer overview | ../expo-platform/expo-modules-core/expo-modules-core.md - native bootstrap
Always add Expo modules through the CLI so semver ranges match the SDK compatibility matrix.
# One or more packages - versions resolved for SDK 57
npx expo install expo-notifications expo-location
# After manual package.json edits or an SDK bump
npx expo install --fix
# Verify no version skew before your first native build
npx expo doctor{
"dependencies": {
"expo": "~57.0.4",
"expo-notifications": "~0.32.0",
"expo-location": "~19.0.0"
}
}npx expo install reads your expo version and picks tested ranges - npm install expo-camera@latest can pull SDK-incompatible native code--fix reconciles every Expo dependency after upgrading expo in package.jsonexpo doctor flags duplicate React Native copies, wrong module versions, and missing config pluginspackage-lock.json / yarn.lock so CI installs the same native ABI your laptop builtRelated: ./expo-notifications.md - first push notification install | ./expo-location.md - location permissions
The expo field in package.json is the single source of truth every other Expo package must satisfy.
SDK 57 contract
expo ~57.0.4 ← root SDK pin
├── react 19.2.3 ← paired by create-expo-app
├── react-native 0.86.0
├── expo-camera ~17.x ← resolved by expo install
├── expo-file-system ~19.x
└── … ← all modules validated against SDK 57 matrix# Intentional SDK bump - read the changelog first
npx expo install expo@~57.0.4
npx expo install --fix
npx expo prebuild --clean # when native folders existexpo causes native compile errors - the matrix is not advisory~57.0.4 allows patch releases within SDK 57 - pin intentionally for reproducible CI--fix before debugging runtime crashes - version skew often surfaces as obscure native exceptionsios/, android/) must be regenerated or rebuilt after SDK jumpsRelated: ../expo-platform/upgrading-expo-sdk-versions/upgrading-expo-sdk-versions.md - upgrade runbook
Expo modules export a namespace or named classes - import once, call async APIs from effects or handlers.
npx expo install expo-hapticsimport * as Haptics from "expo-haptics";
import { Pressable, StyleSheet, Text, View } from "react-native";
export default function App() {
return (
<View style={styles.container}>
<Pressable
style={styles.button}
onPress={() => Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium)}
>
<Text style={styles.label}>Tap for haptic feedback</Text>
</Pressable>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: "center", alignItems: "center" },
button: { backgroundColor: "#2563eb", paddingHorizontal: 20, paddingVertical: 12, borderRadius: 10 },
label: { color: "#fff", fontWeight: "600" },
});@types/ package needed for first-party Expo modulesRelated: ../rn-fundamentals/platform-specific-code/platform-specific-code.md -
Platform.OSguards
Many SDK packages require manifest entries - declare them in app.json or app.config.ts before requesting runtime permission.
npx expo install expo-camera expo-location// app.config.ts
import type { ExpoConfig } from "expo/config";
export default (): ExpoConfig => ({
name: "SdkBasics",
slug: "sdk-basics",
plugins: [
[
"expo-camera",
{
cameraPermission:
"Allow SdkBasics to take photos for work order attachments.",
microphonePermission:
"Allow SdkBasics to record audio with video inspections.",
},
],
[
"expo-location",
{
locationAlwaysAndWhenInUsePermission:
"SdkBasics uses your location to tag field visits even when the app is in the background.",
locationWhenInUsePermission:
"SdkBasics uses your location to show nearby job sites.",
},
],
],
});Info.plist usage descriptions and Android permission rationale - App Review rejects vague copyrequestPermissionsAsyncRelated: ./expo-camera-and-expo-image-picker.md - capture permissions | ./expo-location.md - disclosure strings
Expo Go bundles a fixed set of native modules. Packages with custom native code or unpublished plugins need a dev client.
npx expo install expo-dev-client
npx expo run:ios
# or cloud dev client:
npx eas build --profile development --platform ios┌────────────────────┬─────────────────────┬──────────────────────────┐
│ Capability │ Expo Go │ Development build │
├────────────────────┼─────────────────────┼──────────────────────────┤
│ expo-notifications │ Limited push setup │ Full push + custom icons │
│ expo-camera │ Yes │ Yes + custom plugins │
│ Config plugins │ Ignored │ Applied at prebuild │
│ expo-updates │ Limited │ Production OTA channel │
└────────────────────┴─────────────────────┴──────────────────────────┘expo-dev-client replaces Go as the native shell while keeping Metro fast refreshnpx expo start opens Go but your feature needs a plugin, install dev-client before debugging "module not found" native errorsRelated: ../expo-platform/expo-go-vs-development-builds/expo-go-vs-development-builds.md - feature matrix
Some modules require registration before React mounts - background tasks, notification handlers, and update listeners belong in global scope.
npx expo install expo-notifications expo-task-manager// index.ts - side-effect imports BEFORE registerRootComponent
import "./src/notifications/notificationHandler";
import "./src/background/locationTask";
import { registerRootComponent } from "expo";
import App from "./App";
registerRootComponent(App);// src/notifications/notificationHandler.ts
import * as Notifications from "expo-notifications";
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowBanner: true,
shouldShowList: true,
shouldPlaySound: false,
shouldSetBadge: false,
}),
});TaskManager.defineTask must run in global scope - defining inside a component fails on cold start from the OSapp/_layout.tsx top-level imports for the same side effectsRelated: ./expo-task-manager-and-background-tasks.md - global task registration | ./expo-notifications.md - foreground handler
Expo autolinking discovers native modules from package.json and wires them into generated ios/ and android/ projects.
npx expo prebuild
# inspect native projects without opening Xcode
ls ios/Pods 2>/dev/null || ls android/app/build.gradle{
"expo": {
"plugins": ["expo-notifications"]
}
}npx expo prebuild generates native folders from app.config + installed modules - CNG keeps them out of git in managed workflowexpo-module.config.json in each package - manual pod install edits get overwritten on next prebuildInfo.plist, when using CNGnpx expo install for version safety - autolinking works the sameRelated: ../cng-prebuild/prebuild-basics/prebuild-basics.md - when to prebuild | ../native-modules/native-modules-basics/native-modules-basics.md - module architecture
Some SDK packages ship both a modern API and a /legacy entry - migrate deliberately, not in one PR.
npx expo install expo-file-system// Modern SDK 57 API - File, Directory, Paths
import { Directory, File, Paths } from "expo-file-system";
const cacheDir = new Directory(Paths.cache, "downloads");
cacheDir.create();
const meta = new File(cacheDir, "manifest.json");
meta.create();
meta.write(JSON.stringify({ version: 1 }));
// Legacy API - still available during migration
import * as FileSystem from "expo-file-system/legacy";
const legacyUri = meta.uri;
const text = await FileSystem.readAsStringAsync(legacyUri);File / Directory / Paths for new code on SDK 57 - object-oriented paths reduce URI string bugsexpo-file-system/legacy only for unmigrated call sites - plan removal per releaseexpo release notesRelated: ./expo-file-system.md - sandboxed paths and downloads
Use app.config.ts and EAS environments to vary permissions, plugins, and API keys - not if (__DEV__) forks in production paths.
// app.config.ts
const IS_DEV = process.env.APP_VARIANT === "development";
export default {
name: IS_DEV ? "SdkBasics (Dev)" : "SdkBasics",
slug: "sdk-basics",
extra: {
eas: { projectId: process.env.EAS_PROJECT_ID },
pushEnabled: !IS_DEV,
},
plugins: [
...(IS_DEV ? [] : [["expo-notifications", { icon: "./assets/notification-icon.png" }]]),
],
};import Constants from "expo-constants";
const pushEnabled = Constants.expoConfig?.extra?.pushEnabled ?? false;extra is available at runtime via expo-constants - suitable for feature flags resolved at build timeEXPO_PUBLIC_* inlines into JS - never put secrets there; API URLs only when intentionally publicRelated: ../expo-platform/app-json-and-app-config-js/app-json-and-app-config-js.md - dynamic config
npm install ignores the SDK compatibility table - use it only for non-Expo packages (zod, @tanstack/react-query).expo-* package, always npx expo install <package>.npx expo prebuild or a new EAS build.npx expo install expo-camera
npx expo doctorStack versions: This page was written for React 19.2.3, React Native 0.86.0, and Expo SDK 57 (
expo~57.0.4).
Reviewed by Chris St. John·Last updated Jul 16, 2026