App Store Review Rejections in 2026: The React Native Patterns That Keep Getting Flagged
A field report on the App Store and Play Store rejection patterns we see hit React Native and Expo apps most often in 2026, with concrete fixes before you resubmit.

Shipping a React Native app in 2026 is easier than it has ever been. Getting it through review is not. Over the last twelve months we have watched Apple and Google tighten the screws on exactly the patterns Expo and RN teams love most: fast JS updates, lean MVPs, and third-party SDKs bolted on in a sprint.
This is a field report. Not a legal reading of the guidelines, just the rejection reasons we have personally argued with review boards about, and what actually got the app approved.
The rejection categories that dominate RN apps in 2026
If you look at a year of rejection emails across a portfolio of React Native and Expo apps, the same handful of guideline numbers come up again and again. The specific wording changes, but the themes are stable:
- Apple 4.3(a) — spam / "this app is too similar to other apps you've submitted"
- Apple 2.1 — app completeness, usually broken demo accounts or placeholder content
- Apple 3.1.1 — in-app purchase bypass, now including subtle web-link flows
- Apple 5.1.1(v) — account creation without an in-app delete path
- Google's "Limited Use" policy — on sensitive permissions, especially background location and
QUERY_ALL_PACKAGES - Google Data Safety mismatch — your declared data collection does not match what the SDKs actually do
React Native projects get flagged on these more than native projects do, and not because the framework is suspect. It is because RN teams tend to ship faster, lean on more third-party SDKs, and reuse large chunks of code across white-label builds. All three habits map directly onto the rules above.
4.3(a): the white-label trap
This is the number one reason we see RN agencies and product studios get stuck. You build a core app, then ship four customer variants with different branding, different content, and the same shell. Apple's reviewers have gotten dramatically better at spotting this in 2026, partly because they appear to be hashing bundle structure and asset layouts.
The pattern that triggers it:
- One Expo monorepo with a
shared/package - Multiple
app.config.tstargets with differentname,slug,bundleIdentifier - Nearly identical screens, with content pulled from a CMS
What actually gets you unblocked
Fighting 4.3(a) with an appeal rarely works. What works is proving the apps are substantively different at the binary level, not just at the content level. Things we have done that moved the needle:
- Ship meaningfully different feature sets per client, not just different theme tokens. If Client A has booking and Client B does not, tree-shake it out of the bundle.
- Consolidate truly identical white-labels into a single app with in-app brand switching, and pitch that as the product.
- Use different developer accounts when the end customer is genuinely a different legal entity, and have them sign the submission. This is the cleanest path for agency work.
If you are running a franchise or multi-location play, the single-app-with-locations model is almost always the right answer in 2026. We wrote more about structuring multi-tenant mobile products on the 72Technologies mobile services page.
2.1: the demo account and "it works on my simulator" problem
Apple reviewers test on physical devices behind what appears to be a proxied network. If your login flow depends on SMS OTP to a specific country code, or your backend rate-limits by IP, your reviewer will see a broken app and reject under 2.1.
Things that get flagged in RN apps specifically:
- Hermes crash on first launch because a native module was linked but not initialized for the review build variant
- Expo config plugins that work in dev but fail in a production prebuild
- Feature flags defaulting to
falsefor unknown users, hiding the entire app from the reviewer
A review-build checklist worth stealing
// app.config.ts
export default ({ config }) => ({
...config,
extra: {
reviewMode: process.env.APP_REVIEW === '1',
demoUser: process.env.APP_REVIEW === '1'
? { email: 'review@example.com', bypassOtp: true }
: undefined,
},
});
Then in your auth screen:
import Constants from 'expo-constants';
const { reviewMode, demoUser } = Constants.expoConfig?.extra ?? {};
async function signIn(email: string, code: string) {
if (reviewMode && email === demoUser?.email && code === '000000') {
return signInAsDemo();
}
return normalSignIn(email, code);
}
Two rules: never ship this to a non-review build (gate it in CI by checking the environment variable only exists in the review lane), and rotate the bypass code between submissions. Reviewers do not store it, but automated scanners you do not control might.
3.1.1: in-app purchase bypass is now interpreted broadly
Since the external-link entitlement rules settled in late 2025, reviewers have been much stricter about the gray zone. In RN apps the typical failure pattern is: a user clicks "Upgrade", you open a WebBrowser.openBrowserAsync to a pricing page that happens to include a Stripe link.
Even if you never intended to bypass IAP, if that page offers a cheaper plan than what you sell through StoreKit, you will be rejected.
Safer patterns we use now:
- Keep all consumer-facing subscription purchases inside StoreKit / Google Play Billing via a layer like RevenueCat.
- If you must link out for a B2B tier, make the destination page clearly not consumer-oriented (SSO login, seat management, invoicing language).
- Never show prices in-app for the web-only tier. Describe the tier, link out, and let pricing live on the web.
5.1.1(v): account deletion is not optional anymore
This has been a requirement for years, but in 2026 reviewers are testing it. If your account deletion path is "email us", you will be rejected. If it is a deep link to a web page, they will click it, and if the page requires re-login they often reject anyway.
Ship an in-app flow. Two screens, a confirm step, a call to your backend that soft-deletes immediately and hard-deletes within thirty days. Mention the thirty-day window in the UI. Done.
Google: Data Safety and sensitive permissions
Google's rejection style is different. You will not get a human arguing with you. You will get a policy violation email with a link to a form, and your release will sit in limbo.
The two most common triggers for RN apps:
Data Safety form drift
You declared that you do not collect advertising ID. Then you added a marketing SDK three sprints ago that quietly does. The Play Console's automated scan catches this on your next release. The fix is boring but critical: own a data-collection.md in your repo that lists every SDK, what it collects, and which Data Safety category it maps to. Update it in the same PR that adds or removes the SDK.
Background location and QUERY_ALL_PACKAGES
If any transitive native dependency requests ACCESS_BACKGROUND_LOCATION or QUERY_ALL_PACKAGES, you need to either justify it in the Play Console form or strip the permission. In Expo, do this in your config plugin or app.config.ts:
android: {
permissions: ['ACCESS_FINE_LOCATION'],
blockedPermissions: [
'ACCESS_BACKGROUND_LOCATION',
'QUERY_ALL_PACKAGES',
],
},
Then verify with aapt dump permissions on the actual AAB before you upload. We have been burned by a native module adding a permission in a point release that we did not notice until Google flagged it.
How to actually respond to a rejection
A few things we have learned about the appeals process itself:
- Reply within 24 hours. Reviewers remember context, and your reply sits in the same thread.
- Attach a short screen recording for anything behavioural. Loom links work. Written descriptions of UI flow almost never land.
- Do not argue the guideline. Argue the facts of your app. Reviewers do not have authority to reinterpret policy, but they do decide whether the facts fit.
- If you are confident you are right, escalate to the App Review Board. It is slower, but the quality of reading is noticeably higher.
For Google, skip the generic appeal and use the policy-specific contact form if one exists for your violation. Generic appeals go into a queue that moves in geological time.
Where we'd start
If you are about to submit a React Native or Expo app in the next two weeks, do three things first. Audit every native permission in your final AAB and IPA, not your source. Verify your Data Safety declarations against the actual SDK list. And run through your app once as a brand-new user on a device that has never seen your backend, with a demo account whose credentials you can paste into App Store Connect. Most rejections we see would have been caught by that last step alone.
Want a team like ours?
72Technologies builds production software for the kind of teams who actually read this blog.
Start a projectKeep reading

Push Notifications in React Native 2026: Expo Notifications, FCM HTTP v1, and the APNs Gotchas
A practical breakdown of push on React Native in 2026: Expo Notifications vs raw FCM/APNs, token lifecycles, HTTP v1 migration pain, and the silent failures that eat your delivery rates.

EAS Update in 2026: A Field Guide to OTA Rollouts That Don't Brick Your App
OTA updates in React Native sound like a cheat code until you ship a bad JS bundle to 40,000 devices at 2am. Here's how we structure EAS Update channels, rollouts, and rollbacks so that never happens twice.

In-App Purchases in React Native 2026: RevenueCat, StoreKit 2, and the Receipts That Lie
A field guide to shipping subscriptions in React Native in 2026 — what RevenueCat gets right, where StoreKit 2 still bites, and the receipt edge cases that quietly refund your MRR.
