Automate App Store Submission: A React Native Guide
Learn how to automate App Store submission for React Native & Expo apps. This end-to-end guide covers CI/CD with Fastlane, EAS, and handling rejections.

You've got the build passing in CI. The .ipa uploads. Maybe TestFlight is green. Then release day stalls on the part everyone underestimates: metadata, screenshots, privacy fields, reviewer notes, export compliance, login instructions, and one vague rejection that no script can interpret safely.
That's the problem when you try to automate app store submission. The binary pipeline is usually the easy part. The last mile is where teams lose hours, ship late, or fall back to manual clicks in App Store Connect because the process still has hidden human checkpoints.
In React Native projects, this gets messier fast. You're often juggling native signing, JavaScript release notes, Expo or bare workflow differences, and store requirements that change more often than your core app architecture. Good automation helps. Blind automation breaks.
Table of Contents
- Understanding the Automation Landscape
- Choosing Your Automation Engine: Fastlane vs EAS
- Building Your CI/CD Submission Pipeline
- Automating Store Listings and Compliance
- Navigating Rejections and Advanced Release Strategies
- DIY vs Done-For-You: When to Call in the Experts
Understanding the Automation Landscape
App store automation is often perceived as meaning one command that sends a build to Apple or Google. In practice, it's four separate systems that need to cooperate: build the binary, sign it correctly, upload it to the store, then submit a version that includes all required metadata and review details.
If any one of those four parts is brittle, your “automated” release turns into a Slack fire drill. The signing certificate expired. The uploaded build isn't attached to the version. The privacy data doesn't match what the store expects. The reviewer can't log in. None of that is fixed by a green CI badge.

The four parts that matter
A reliable workflow usually looks like this:
Build Your CI system creates a release artifact from a tagged commit or release branch. For React Native, that means keeping native dependencies, environment variables, and release configuration stable enough that the build is reproducible.
Sign Many pipelines become fragile during this step. Automatic signing is fine until certificates, profiles, or keychain access drift out of sync across local machines and CI runners.
Upload Uploading is mechanical, but still worth isolating from submission. A successful upload only means the artifact reached the platform. It does not mean the store listing is ready.
Submit Submission is the last mile. At this stage, metadata, screenshots, privacy information, export compliance answers, review notes, and release strategy all have to line up.
Practical rule: Separate “build uploaded” from “ready for review” in your pipeline. Those are different states, and treating them as one step hides failure points.
What changed with App Store Connect
Apple's tooling used to be much worse for automation. Before the modern API coverage, teams often relied on GUI scripting, reverse-engineered flows, or older upload tools that didn't expose the full submission lifecycle cleanly.
That changed when Apple expanded the App Store Connect API in 2020 to cover the submission workflow, including the App Store Version Submission resource that lets developers trigger submission through v1/appStoreVersionSubmissions instead of a manual click in the web UI, as shown in Apple's WWDC App Store Connect API update. That shift matters because it made full workflow automation realistic instead of fragile.
A good mental model is simple: official APIs handle the machine-work well now. They don't remove the policy-work. Your scripts can create versions, attach metadata, and submit. They can't explain to an Apple reviewer why your login flow is confusing or why your screenshots don't match the actual app state.
That gap is where most release pipelines still crack.
Choosing Your Automation Engine: Fastlane vs EAS
React Native teams usually land on Fastlane, EAS Submit, or a mix of both. Neither is universally better. The right choice depends on how much native control you need, how much maintenance you're willing to own, and whether your project lives comfortably in Expo's ecosystem.
Where Fastlane wins
Fastlane is still the most flexible option when you need custom release logic. If you maintain a bare React Native app, use multiple targets, have custom signing rules, or want full control over screenshots, metadata, and lane logic, Fastlane gives you room to script almost everything.
That flexibility comes with overhead. Fastfile drift is real. Signing setup can get ugly. When something breaks, it often breaks in Ruby, shell, Xcode, and App Store Connect at the same time.
Fastlane is strongest when your team wants to encode a release process as a set of explicit steps:
- Custom lane logic: Branch-specific behavior, preflight checks, screenshot generation, metadata sync, and release gates all fit naturally.
- Bare React Native support: It doesn't care whether you use Expo, bare native modules, custom build phases, or unusual target layouts.
- Deep store workflow coverage: It works well when you need more than just upload. That includes listing updates and version management.
Where EAS wins
EAS Submit is much easier to adopt if you already use Expo build infrastructure or want a simpler path from build artifact to store upload. The command surface is small, the setup is cleaner, and teams can avoid a lot of custom CI plumbing.
The happy path is straightforward. Install the CLI, authenticate, and run eas submit --platform ios. Expo's EAS submission guide covers that workflow directly.
The catch is that EAS doesn't erase store-review realities. One of the most common mistakes is forgetting to provide demo account credentials in App Review Information for apps that require login. Expo's documentation notes that this pitfall can lead to a near-100% rejection rate for those cases, which is why any serious automation flow needs reviewer-facing information, not just build automation.
Automation that uploads a private app without valid review credentials isn't a release pipeline. It's a rejection generator.
Fastlane vs EAS Submit at a Glance
| Criterion | Fastlane | EAS (Expo Application Services) |
|---|---|---|
| Setup complexity | Higher. More moving parts, more control. | Lower on the happy path, especially with Expo-managed apps. |
| React Native fit | Excellent for bare React Native and complex native setups. | Best when the project already uses Expo tooling comfortably. |
| Code signing control | Strong, but you own the complexity. | Simpler operationally, with less custom control. |
| Customization | Very high. Good for teams with unusual release requirements. | Moderate. Good for standard pipelines. |
| Metadata and listing workflow | Better suited when you want to script more of the full release process. | Good for upload and submit flow, but many teams still need extra handling around listing content. |
| Maintenance burden | Higher over time. | Lower if your app stays within Expo-friendly patterns. |
| Best fit | Teams that want release engineering control. | Teams that want speed and less infrastructure work. |
A practical rule works well here:
- Pick Fastlane if release engineering is part of your product discipline.
- Pick EAS if you want a cleaner default and your app architecture doesn't fight Expo.
- Use both if you want Expo builds but still need custom scripting around metadata, screenshots, or approval gates.
The wrong choice isn't usually technical failure. It's choosing a tool that your team won't maintain six months from now.
Building Your CI/CD Submission Pipeline
The best submission pipelines are boring. They trigger from a predictable branch or tag, pull secrets from the CI platform, build exactly one release configuration, and leave an audit trail that another engineer can follow without asking who pressed which button.

A pipeline that survives real releases
GitHub Actions provides sufficient capabilities for common use cases. So are Bitrise, CircleCI, and GitLab CI. The mechanics differ, but the shape should stay the same:
- Trigger deliberately: Use release branches or tags. Don't wire store submission to every push to
main. - Store secrets centrally: Keep App Store Connect credentials, signing material, and Expo tokens in encrypted CI secrets, not in the repo.
- Split jobs by responsibility: Build first. Upload second. Submit only after validation passes.
- Keep outputs visible: Archive logs, build artifacts, and generated metadata so failures are debuggable.
A thin GitHub Actions example for Fastlane looks like this:
name: ios-release
on:
push:
branches:
- 'release/*'
jobs:
submit-ios:
runs-on: macos-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Install Node dependencies
run: npm ci
- name: Install iOS dependencies
run: cd ios && bundle exec pod install
- name: Run Fastlane
env:
APP_STORE_CONNECT_API_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY }}
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
run: bundle exec fastlane ios submit
An EAS-based job is even shorter:
name: eas-submit-ios
on:
push:
branches:
- 'release/*'
jobs:
submit-ios:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install dependencies
run: npm ci
- name: Install EAS CLI
run: npm install -g eas-cli
- name: Submit to App Store
env:
EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }}
run: eas submit --platform ios
A practical GitHub Actions shape
Keep one rule above all others: never let the pipeline invent missing business information. If review notes, demo credentials, privacy policy URLs, or metadata fields are absent, fail fast.
A preflight step is worth adding before any submit command:
test -n "$APP_PRIVACY_POLICY_URL" || (echo "Missing privacy policy URL" && exit 1)
test -n "$APP_REVIEW_DEMO_USER" || (echo "Missing review demo user" && exit 1)
test -n "$APP_REVIEW_DEMO_PASSWORD" || (echo "Missing review demo password" && exit 1)
If you want a visual walkthrough of CI wiring and release flow, this clip is a useful complement to the YAML examples:
Keep the final approval gate human
I've had the fewest release-day surprises with pipelines that stop at “ready to submit” unless the release is routine and low-risk. That means the machine assembles everything, validates everything it can, and a human still confirms the final state of the listing.
The strongest CI/CD setup isn't the one with the fewest humans. It's the one that asks for human input only when judgment is required.
That keeps the automation useful without pretending the review process is deterministic.
Automating Store Listings and Compliance
Most failed submissions aren't caused by compilation. They're caused by missing or invalid listing data. This is why serious teams treat store metadata as versioned project assets, not as text someone pastes into a dashboard at the end of a sprint.

Treat listing content like code
A simple structure works:
store/listings/en-US/description.txtfor the long descriptionstore/listings/en-US/promo.txtfor promotional textstore/listings/en-US/keywords.txtfor App Store keywordsstore/release-notes/ios.txtfor “What's New”store/review-notes/ios.mdfor reviewer instructionsstore/privacy/for privacy policy references and manifest-related checks
This buys you three things. You get version history, peer review, and reproducibility. It also makes localization manageable because every language variant follows the same structure.
Validate before the store does
Some requirements are hard validation rules, so your pipeline should enforce them before upload or submission. Modern App Store rules include a 30-character app name limit, and since 2024 a valid Privacy Policy URL is mandatory for all App Store submissions to avoid guaranteed rejection, as summarized in this overview of current App Store publishing requirements.
That means your automation should check content boundaries and required fields in code, not in a human checklist alone.
A lightweight preflight script can catch a lot:
APP_NAME="$(cat store/listings/en-US/name.txt)"
PROMO_TEXT="$(cat store/listings/en-US/promo.txt)"
DESCRIPTION="$(cat store/listings/en-US/description.txt)"
[ ${#APP_NAME} -le 30 ] || (echo "App name too long" && exit 1)
[ ${#PROMO_TEXT} -le 170 ] || (echo "Promotional text too long" && exit 1)
[ ${#DESCRIPTION} -le 4000 ] || (echo "Description too long" && exit 1)
[ -n "$APP_PRIVACY_POLICY_URL" ] || (echo "Privacy Policy URL missing" && exit 1)
For iOS, privacy work also isn't optional anymore. The privacy manifest and declared API usage need to agree with what you submit. If your app uses the newer privacy manifest flow, keep those files under source control and make release validation compare them against the store-facing declarations.
If the store can reject a field automatically, your CI should reject it earlier.
Screenshots are still operational work
Teams usually discover that “submission automation” doesn't mean “creative asset automation is solved.” Screenshot generation can be scripted, but only if your app can reliably reach the right UI states in simulators or seeded demo accounts.
For React Native apps, the practical pattern is:
- Automate capture where UI is stable: onboarding, settings, dashboards with seeded fixtures
- Manual review for visual sanity: text clipping, dark mode issues, locale mismatch, stale mock data
- Store device-specific output in predictable folders: one directory per platform, locale, and screen family
Fastlane's screenshot tooling is still useful here, but the important part isn't the tool. It's whether your app can boot into deterministic states without relying on live APIs or human tapping.
Keywords deserve the same discipline. Apple keyword formatting has its own quirks, including the 100-character comma-delimited keyword format without spaces and other submission details discussed in this long-running developer discussion on automating submission steps. If your pipeline writes keywords automatically, validate formatting before the upload phase instead of debugging it in App Store Connect.
Navigating Rejections and Advanced Release Strategies
Release night often fails in boring ways. The build uploads, signing passes, metadata validates, and then App Review rejects the binary because the demo account is locked, the reviewer cannot reach a paywalled screen, or your privacy answer does not match what the app does.
That is the last mile problem with store automation. CI can assemble the app, run tests, push metadata, and submit the build. It cannot explain intent to a reviewer who is looking at your app for five minutes with incomplete context.
A significant share of rejections come from issues that require human interpretation rather than build automation. In practice, the repeat offenders are reviewer access, incomplete notes, misleading metadata, privacy disclosures that are technically true but phrased badly, and features that only make sense if someone explains the expected flow first.
Treat review prep as part of the pipeline, not as an afterthought in someone's head. The process that holds up best includes:
- Reviewer notes written like internal runbooks: exact steps, expected results, known limitations, and any region, role, or feature-flag conditions
- Fresh test credentials: accounts monitored before submission, with MFA disabled or clearly documented if Apple or Google needs a one-time code path
- A rejection owner: one engineer or release manager who replies, gathers evidence, and keeps the resubmission from turning into a Slack scavenger hunt
- Versioned policy artifacts: privacy text, data-use declarations, age rating answers, and support URLs tracked alongside the release, not edited ad hoc in the console
I have seen fully automated pipelines stall for a day because the app was correct and the explanation was missing.
Automation ends before reviewer subjectivity
Reviewers do not test like your team tests. They use sparse notes, unfamiliar devices, and account states you did not plan for. That is why borderline cases keep slipping through automation. Subscription copy may read fine to product and legal, then get flagged as misleading. A permissions prompt may be justified in the app, but still look premature if it appears before the user takes an action that makes the need obvious.
The right response is not more scripts. It is better reviewer context and faster human follow-up.
Keep a standard rejection package ready: screen recording, annotated screenshots, test account details, backend logs if access failed, and the exact guideline your team believes the app satisfies. That shortens the appeal or resubmission cycle and keeps emotion out of the reply.
Rejection handling is part release engineering, part policy interpretation, and part support work.
Use phased rollout controls with intent
Approval and release should stay separate for any update that touches risky surfaces. Apple's official App Store Connect documentation supports phased release for automatic updates, which lets you release over time instead of sending the update to everyone at once. Use that control when the blast radius is hard to predict, not because a generic blog said to.
Phased rollout is useful when:
- Backend behavior changed: review may pass while production traffic exposes contract mismatches or stale feature flags
- Auth, billing, or onboarding changed: these paths create the ugliest failures and the highest support volume
- Large UI rewrites shipped: approval does not mean the new flow is obvious to users on every device and locale
- You depend on remote config: a bad flag can hurt approved builds just as easily as broken code
For Google Play, staged rollouts serve the same purpose. For iOS, pair App Store release controls with disciplined TestFlight testing before submission. The practical rule is simple. If your team cannot reproduce the exact reviewer path on demand, do not expect first-pass approval to be predictable.
Full automation reaches its limit when store policy, wording, and human judgment take over. That is why the strongest submission systems combine automation for the repeatable work with a human owner for reviewer notes, rejection handling, and release decisions.
DIY vs Done-For-You: When to Call in the Experts
There's a point where building your own release machinery stops being an advantage and starts becoming side work. If your team enjoys release engineering, that trade can be worth it. If your team is trying to ship product features, store ops can become a tax that keeps resurfacing every time Apple, Google, Xcode, signing, or privacy rules shift.
DIY works best when you have someone on the team who owns the release surface end to end. Not just CI. The whole chain: signing, metadata, screenshots, reviewer notes, privacy requirements, rollout settings, and rejection handling. Without that owner, automation usually decays into a set of scripts that “mostly work” until launch week.
A managed option makes sense when delays are expensive, your app needs reviewer hand-holding, or your team doesn't want to keep relearning store policy edge cases. That's especially true for React Native and Expo apps, where the technical stack is already split across JavaScript tooling, native build systems, and store-specific compliance work.

If you'd rather hand off the painful last mile, including screenshots, listing copy, privacy policy hosting, reviewer notes, resubmissions, and the back-and-forth with the stores, a specialist service can be the simpler call. The value isn't just speed. It's reducing launch uncertainty.
If you want a team to handle the messy parts of automating app store submission for your React Native or Expo app, LetsDeployIt is built for exactly that. They manage store assets, compliance checks, reviewer communication, and end-to-end submission so your team can stay focused on shipping the product instead of wrestling with App Store Connect and Google Play.