Skip to content

Guide · 9 min read · Updated 2026-10-06

Expo App Store submission checklist

Most Expo submissions that bounce don’t fail on code quality. They fail on configuration: a placeholder bundle ID, a missing permission string, a development build profile or a paywall without a restore button. Work through this list before you run eas submit.
Check your Expo app before submitting →AppCheck tests many of these items automatically. Free, local, no account.

1. Identity: name, bundle identifier and package

Your iOS bundle identifier and Android package name are permanent once you upload a build. Choose them deliberately, based on a domain you control.

  • expo.name is set to the name you want under the icon.
  • expo.ios.bundleIdentifier is set, e.g. com.yourdomain.appname — not com.example, com.anonymous or host.exp.exponent.
  • expo.android.package is set and follows the same reverse-domain convention.
  • The bundle identifier matches the App ID you registered in your Apple Developer account and the app record in App Store Connect.
  • Firebase, RevenueCat, Google Sign-In and OAuth providers are configured with the same identifiers.

2. Versioning and build numbers

App Store Connect rejects an upload that reuses a build number for the same version. Google Play requires each upload to have a higher versionCode.

  • expo.version is set using semantic versioning (e.g. 1.0.0).
  • Either set "cli": { "appVersionSource": "remote" } in eas.json and "autoIncrement": true in the production profile, or manage ios.buildNumber and android.versionCode manually.
  • If you use EAS Update, expo.runtimeVersion is configured so updates only reach compatible builds.

3. EAS build profiles

  • eas.json exists and contains a build.production profile.
  • The production profile does not set developmentClient: true.
  • The production profile does not use distribution: "internal" or ios.simulator: true.
  • Android production builds produce an AAB (the default), not an APK.
  • Environment variables for production (API URLs, public keys) are set per profile, not hard-coded.
  • Run npx expo install --fix so React Native and Expo packages match your SDK.

4. Icons, splash and display

  • expo.icon points to a 1024×1024 PNG with no transparency, and the file exists.
  • expo.android.adaptiveIcon has a foregroundImage with safe-zone padding and a backgroundColor.
  • A splash screen is configured (the expo-splash-screen plugin on SDK 52+).
  • expo.scheme is set if you use Expo Router, deep links or OAuth redirects.

5. Permissions and purpose strings

iOS requires a purpose string for every protected resource you request, and App Review reads them. Each should explain specifically why your app needs access — “We need your camera” is weaker than “Scan receipts to add expenses”.

  • Camera → NSCameraUsageDescription (or the expo-camera plugin’s cameraPermission).
  • Photo library → NSPhotoLibraryUsageDescription (expo-image-picker photosPermission).
  • Microphone → NSMicrophoneUsageDescription, if you record audio or video.
  • Location → NSLocationWhenInUseUsageDescription; justify any background location.
  • Contacts, calendars → NSContactsUsageDescription, NSCalendarsUsageDescription.
  • Remove Android permissions you don’t use, especially SMS, call log, QUERY_ALL_PACKAGES and background location.

6. Privacy: tracking, manifests and labels

  • If any SDK tracks users across apps or websites (e.g. personalised ads), request App Tracking Transparency permission first and include NSUserTrackingUsageDescription.
  • Configure expo.ios.privacyManifests with the required-reason APIs your app and SDKs use.
  • Complete App Privacy details in App Store Connect to reflect analytics, crash reporting and advertising SDKs.
  • Publish a privacy policy URL and add it to App Store Connect.
  • If your app supports account creation, provide in-app account deletion.

7. In-app purchases and subscriptions

Apple requires digital goods and subscriptions to use in-app purchase. Reviewers will try to buy, cancel and restore.

  • A visible Restore Purchases button exists, typically on the paywall and in settings.
  • Purchase calls handle user cancellation and errors without leaving the UI stuck.
  • Premium state is derived from entitlements, not a hard-coded flag.
  • RevenueCat is configured with the platform public keys (appl_ / goog_), not a test or secret key.
  • Subscription terms, price and a link to your terms and privacy policy are shown on the paywall.
  • Products are “Ready to Submit” in App Store Connect and attached to the version under review.

8. Security and production configuration

  • No secret keys in the app: Supabase service-role keys, Stripe sk_live_ keys, private keys or service-account JSON.
  • No EXPO_PUBLIC_ variable holds a secret — Expo inlines them into the bundle.
  • Tokens are stored in expo-secure-store, not AsyncStorage.
  • No localhost, LAN or ngrok URLs outside __DEV__ guards; all endpoints use HTTPS.
  • No AdMob test ad unit IDs outside __DEV__, and the AdMob app ID is configured.
  • Debug and mock flags are derived from __DEV__ or build profiles, not hard-coded to true.

9. App Store Connect essentials

  • Screenshots for the required device sizes, reflecting the current UI.
  • Description, keywords, support URL and age rating questionnaire completed.
  • Export compliance: set ios.config.usesNonExemptEncryption appropriately to skip the per-build question.
  • A demo account and notes for App Review if your app requires sign-in.
  • Test the production build on a real device via TestFlight before submitting.

Before you submit

This checklist reflects common submission requirements as generally documented by Apple and Expo. Requirements change; always confirm against Apple’s current App Review Guidelines and Expo’s documentation for your SDK version. Passing this checklist does not guarantee approval.

Check your Expo app before submitting →AppCheck tests many of these items automatically. Free, local, no account.