Expo tutorial
Publish an Expo app: prebuild, release build, and store checklist
This guide prepares an Expo project for App Store and Google Play submission through Shiptavio. It covers app configuration, native generation, public environment values, release builds, and the files to send.
By Shiptavio · Updated 26 October 2026 · 10 min read · Expo
1. Confirm the app is a fit
Shiptavio publishes free apps under the Apple Developer and Google Play organization accounts of our Malaysia-based organization. Check the commercial and category limits first.
- The app is free. No paid download, in-app purchase, or digital subscription.
- The app is not in a prohibited category.
- Every API uses production HTTPS.
- You own the code, name, logo, and content.
- You can provide a demo account if the app has a login.
2. Complete app.json or app.config.ts
The Expo config is the source of truth for the native project. A missing bundle identifier or package name will stop the build.
- Keep the version, iOS buildNumber, and Android versionCode current.
- Add config plugins for permissions, native modules, deep links, icons, and splash screens.
- If you use app.config.ts, make sure it does not depend on a secret that you cannot share.
- Do not change the bundle identifier or package name after the first store publish.
{
"expo": {
"name": "My App",
"slug": "my-app",
"scheme": "myapp",
"version": "1.2.0",
"ios": {
"bundleIdentifier": "com.yourcompany.myapp",
"buildNumber": "42"
},
"android": {
"package": "com.yourcompany.myapp",
"versionCode": 42
}
}
}3. Generate and check the native project
Expo can generate ios/ and android/ from the config. Shiptavio runs prebuild when the project relies on those generated folders, but a local prebuild catches plugin errors early.
- If the project commits ios/ and android/, include them in the source.
- If it relies on continuous native generation, include every config plugin and the prebuild command.
- Check that app icons, splash screens, and permission strings survive prebuild.
- Remove generated folders from the zip if they are not part of your source of truth.
npx expo prebuild --cleannpx expo-doctorA clean prebuild is the closest local check to what Shiptavio will run. If it fails, send the full error in the build notes.
4. Handle environment values safely
Expo bakes EXPO_PUBLIC_* values into the app bundle. They are public. Private keys belong on your server, not in the app.
- Use EXPO_PUBLIC_* for public values such as a production API URL.
- Never put a private API key, signing secret, or service-role key in the app.
- Add non-secret production values in the Shiptavio submission form or with --env-file in the CLI.
- If you use EAS secrets, remember that Shiptavio's build does not read your EAS project. Provide the values through the submission form or a runtime config service.
5. Run a native release build
EAS Build is not required for Shiptavio. We build the native project on our Mac. You can still use EAS for your own development builds.
- Use npm ci, yarn install --frozen-lockfile, or pnpm install --frozen-lockfile to match the lockfile.
- EAS Build profiles are optional. If your project depends on EAS-hosted secrets, provide those values through the submission form instead.
- Remove development clients, debug menus, and test deep links from the release build.
npm ci
npx expo prebuild --cleannpx expo run:ios --configuration Release
npx expo run:android --variant releaseThe exact native commands depend on your project. If your build needs a custom script, put it in the build command field.
6. Package the source
Send the Expo source, not a finished binary. Include the config, lockfile, and native folders when they are part of the project.
- Include app.json or app.config.ts, package.json, the lockfile, and source files.
- Include ios/ and android/ if the project commits them.
- Leave out node_modules, .expo, dist/, build output, .env files, keystores, and private keys.
- The source limit is 200 MB.
git add . ':!**/node_modules'
git commit -m "Prepare Expo release"
git archive --format=zip -o my-app-source.zip HEAD7. Submit to Shiptavio
Create the app record, choose Expo, and add the source and listing details.
- Add the exact prebuild and build command.
- Add non-secret production environment values.
- Include icons, screenshots, store text, privacy policy, and a demo account if needed.
- Tell us in the build notes if the app uses EAS Update or another OTA service.
8. Updates, OTA, and common failures
A store update uses one release credit. EAS Update is a separate OTA channel and is not managed by Shiptavio.
- runtimeVersion mismatch: align the store build and the OTA update before you publish either.
- Missing bundle identifier or package: add ios.bundleIdentifier and android.package.
- Config plugin failure: run npx expo prebuild --clean locally and send the error.
- Version code not incremented: both stores reject a duplicate build number.
- New Architecture or Hermes mismatch: document the setting in the build notes.
- Do not send EAS credentials or private signing keys.
Common questions
Can I publish an Expo app without EAS?
Does Shiptavio support EAS Update?
Do I need to commit ios/ and android/?
Are EXPO_PUBLIC_ variables secret?
Can I send a build from EAS?
What happens if prebuild fails?
Related guides
Publish an Expo app
The service overview, scope, and pricing.
React Native publishing tutorial
For bare React Native CLI projects.
Flutter publishing tutorial
For apps built in Dart with Flutter.
How to submit
The dashboard flow from account creation to store review.
Compare publishing options
Shiptavio, Expo EAS, Codemagic, and Fastlane.
Ready to send the project?
Register the app and add the source. A person reviews the project before the build starts.