<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Debojit Das</title>
    <description>The latest articles on DEV Community by Debojit Das (@debojitdas2003).</description>
    <link>https://dev.to/debojitdas2003</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1445972%2F70ec462a-e795-4d04-aa6c-1cd855b5ed92.jpeg</url>
      <title>DEV Community: Debojit Das</title>
      <link>https://dev.to/debojitdas2003</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/debojitdas2003"/>
    <language>en</language>
    <item>
      <title>Local Builds vs. EAS Cloud: When to Use Which?</title>
      <dc:creator>Debojit Das</dc:creator>
      <pubDate>Tue, 16 Jun 2026 09:13:00 +0000</pubDate>
      <link>https://dev.to/debojitdas2003/local-builds-vs-eas-cloud-when-to-use-which-345l</link>
      <guid>https://dev.to/debojitdas2003/local-builds-vs-eas-cloud-when-to-use-which-345l</guid>
      <description>&lt;p&gt;When you look at your terminal history, you can compile your app in two very distinct ways using EAS:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Cloud: &lt;code&gt;eas build --platform ios --profile production&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Local: &lt;code&gt;eas build --platform ios --profile production --local&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both commands yield a production-ready &lt;code&gt;.ipa&lt;/code&gt; or &lt;code&gt;.aab&lt;/code&gt; file. But one runs on a remote server farm, while the other spins up your laptop fans until they sound like a jet engine. Why should you choose one over the other? Let’s break down the technical trade-offs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why You Should Build Locally (The Local Advantage)
&lt;/h3&gt;

&lt;p&gt;Building locally means compiling the code right on your own hardware (like a MacBook or a dedicated mini-server). Here is why this route is incredibly powerful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero Queue Times:&lt;/strong&gt; The free EAS cloud tier places you in a shared public queue. During peak developer hours, you might wait 15 to 30 minutes just for your build to start. Local builds execute instantly the second you hit enter.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bypassing Internet Bandwidth Limits:&lt;/strong&gt; A cloud build requires uploading your entire packed project to Expo's servers. If you are working on a connection with slow upload speeds, uploading a project packed with assets can take forever. A local build stays right where it is.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Infinite Free Builds:&lt;/strong&gt; You are completely limited by your own hardware. You can build 50 binaries a day without worrying about resource credits or billing thresholds.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why You Must Consider the Cloud (The Cloud Advantage)
&lt;/h3&gt;

&lt;p&gt;Despite the convenience of local builds, the cloud version is often the safest, most reliable bet for production. Here is why:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Shared Vault Security:&lt;/strong&gt; Cloud builds read variables straight from your cloud dashboard (&lt;code&gt;eas env&lt;/code&gt;). Local builds strictly obey your &lt;code&gt;.gitignore&lt;/code&gt;, meaning you must constantly ensure your &lt;code&gt;.env.local&lt;/code&gt; files are perfectly in sync or risk compiling an app with missing or completely blank environment variables.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-Platform Independence:&lt;/strong&gt; You cannot compile a native iOS &lt;code&gt;.ipa&lt;/code&gt; file on a Windows machine. If you are building cross-platform apps and don't own an expensive Mac mini server to handle iOS code signing, the EAS cloud is your only viable path forward.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clean Slate Reproducibility:&lt;/strong&gt; Your local machine accumulates configuration cruft over time. A cloud build runs on a sterile, standardized virtual machine instance. If a cloud build passes, you know with 100% certainty that your code is globally reproducible.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Cheat Sheet Matrix
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Preferred Approach&lt;/th&gt;
&lt;th&gt;Why?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rapid Internal Testing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Local Build&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Instant execution, no cloud queue delay.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Final App Store / Play Store Release&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Cloud Build&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Clean cache, handles production &lt;code&gt;eas env&lt;/code&gt; perfectly.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Developing on Windows/Linux&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Cloud Build&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Mandated to compile the iOS binary.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;No Internet / Low Upload Speed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Local Build&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Bypasses massive source code archive uploads.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;p&gt;[ignore keywords: "eas build local vs cloud", "how to run eas build locally", "compile react native app without mac", "advantages of local mobile app builds", "expo build environment configuration"]&lt;/p&gt;

</description>
      <category>expo</category>
      <category>eas</category>
      <category>mobile</category>
      <category>reactnative</category>
    </item>
    <item>
      <title>How to Maximize Profit Out of EAS (And When to Buy the Subscription)</title>
      <dc:creator>Debojit Das</dc:creator>
      <pubDate>Tue, 16 Jun 2026 09:10:46 +0000</pubDate>
      <link>https://dev.to/debojitdas2003/how-to-maximize-profit-out-of-eas-and-when-to-buy-the-subscription-347d</link>
      <guid>https://dev.to/debojitdas2003/how-to-maximize-profit-out-of-eas-and-when-to-buy-the-subscription-347d</guid>
      <description>&lt;p&gt;As an indie hacker building an app ecosystem, every single dollar matters. When you look at Expo’s pricing page, jumping from the Free tier to the &lt;strong&gt;On-Demand&lt;/strong&gt; or &lt;strong&gt;Production&lt;/strong&gt; plans can feel like a steep commitment.&lt;/p&gt;

&lt;p&gt;However, you can stay on the &lt;strong&gt;EAS Free Tier&lt;/strong&gt; for an incredibly long time if you optimize your workflow correctly. Let’s break down how to get maximum value for zero dollars, and the exact moment your business dictates buying a subscription.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Free Tier Optimization Blueprint
&lt;/h3&gt;

&lt;p&gt;Expo's free tier gives you a generous allowance of cloud builds and free OTA update bandwidth. To maximize your profit margins, follow these rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Don't Waste Cloud Build Slots on Bugs:&lt;/strong&gt; Never trigger an &lt;code&gt;eas build&lt;/code&gt; to test if a feature works. Use a local release simulator build (&lt;code&gt;npx expo run:ios --configuration Release&lt;/code&gt;) to catch syntax errors or missing environment variables on your own machine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep Your Assets Lean:&lt;/strong&gt; EAS Update allows a massive amount of free data transfer, but it counts bytes. Compress your images, use SVG icons instead of massive PNGs, and host large assets (like onboarding videos) on external storage instead of embedding them directly in your app bundle.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. When to Move to On-Demand (Pay-As-You-Go)
&lt;/h3&gt;

&lt;p&gt;You do not need a monthly subscription to get out of the free build queues. If your app is growing and you find yourself constantly waiting 20 minutes behind other developers in the free cloud build queue, you can transition to &lt;strong&gt;On-Demand billing&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For a few dollars per build, you can buy high-priority slots that jump you straight to the front of the line. This is ideal when you are actively scaling up TestFlight testing or pushing crucial launch updates.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The Turning Point: When to Buy the $29/mo or $99/mo Plan
&lt;/h3&gt;

&lt;p&gt;There are three specific thresholds where buying a flat monthly EAS subscription becomes mathematically profitable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Team Collaboration:&lt;/strong&gt; If you scale your business and bring in other stakeholders—like a business manager, designer, or public relations lead—managing a shared pipeline requires advanced team controls and automated workflows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Heavy Build Volume:&lt;/strong&gt; If you are managing multiple apps simultaneously and deploying weekly, paying a flat subscription beats paying individual on-demand fees.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enterprise Features / Custom Certificates:&lt;/strong&gt; If you need dedicated concurrency (running multiple builds at the same exact time) or require specific compliance architectures, the subscription pays for itself in time saved.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;Start completely free. Guard your free tier bandwidth by optimizing assets and testing your release binaries locally. Only let real user traction or queue frustration dictate your spending.&lt;/p&gt;




&lt;p&gt;[ignore keywords: "Expo EAS pricing optimization", "How to use Expo free tier in production", "When to upgrade to EAS paid plan", "Expo OTA update bandwidth limits", "Maximize profit mobile app development"]&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>expo</category>
      <category>eas</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Why Use Expo EAS in the First Place? (The Honest Truth)</title>
      <dc:creator>Debojit Das</dc:creator>
      <pubDate>Tue, 16 Jun 2026 09:07:11 +0000</pubDate>
      <link>https://dev.to/debojitdas2003/why-use-expo-eas-in-the-first-place-the-honest-truth-26ga</link>
      <guid>https://dev.to/debojitdas2003/why-use-expo-eas-in-the-first-place-the-honest-truth-26ga</guid>
      <description>&lt;p&gt;When you are a solo developer or running a tiny dev shop, your most valuable asset isn't your code—it's your time.&lt;/p&gt;

&lt;p&gt;For a long time, the standard workflow for building a React Native app looked like this: you open your terminal, type &lt;code&gt;cd android &amp;amp;&amp;amp; ./gradlew bundleRelease &amp;amp;&amp;amp; cd ..&lt;/code&gt; for Android, and then spend another 20 minutes clicking through Xcode archives on a Mac to ship to iOS. It works, it's free, and it feels comfortable.&lt;/p&gt;

&lt;p&gt;So why on earth should you disrupt your setup to migrate to Expo Application Services (EAS)? Is it just a shiny tool, or does it actually change the game?&lt;/p&gt;

&lt;p&gt;Let’s talk about why EAS is practically mandatory for modern mobile developers.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Matrix of Native Dependencies
&lt;/h3&gt;

&lt;p&gt;When you build a standard React Native app locally, you are managing a fragile house of cards: the exact version of CocoaPods, the specific Android Gradle Plugin, your local Java JDK version, and the version of Xcode installed on your machine. If any of these drift out of sync, your build breaks, and you waste three hours on StackOverflow.&lt;/p&gt;

&lt;p&gt;EAS abstracts this entire nightmare away using &lt;strong&gt;EAS Build&lt;/strong&gt;. It spins up temporary, perfectly configured container instances in the cloud to compile your &lt;code&gt;.ipa&lt;/code&gt; and &lt;code&gt;.aab&lt;/code&gt; files. Your local machine's clutter can no longer poison your production bundle.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Over-The-Air (OTA) Updates: The Ultimate Safety Net
&lt;/h3&gt;

&lt;p&gt;Imagine shipping an update to TestFlight or the App Store, and five minutes later, your analytics show that a rogue null pointer is crashing the app for 100% of your users.&lt;/p&gt;

&lt;p&gt;Without EAS, you have to write a patch, re-compile the app, submit a new build, and pray that Apple or Google approves your emergency review within 24 hours.&lt;/p&gt;

&lt;p&gt;With &lt;strong&gt;EAS Update&lt;/strong&gt;, you fix the JavaScript file locally and push an OTA update in seconds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;eas update &lt;span class="nt"&gt;--branch&lt;/span&gt; production &lt;span class="nt"&gt;--clear-cache&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next time the app opens on a user's phone, it silently downloads the fix. You just saved your launch week without waiting for App Store approval.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Apple/Google Credentials Managed on Autopilot
&lt;/h3&gt;

&lt;p&gt;If you’ve ever had to manually generate an iOS Provisioning Profile, configure a distribution certificate, or handle push notification keys in the Apple Developer Portal, you know how painful it is. EAS automates this completely. The first time you run a build, it securely asks for your developer login, hooks up the credentials, and links them perfectly to your cloud profile.&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;You don't use EAS just to build binaries; you use it to buy your peace of mind. It turns a manual, error-prone DevOps pipeline into a standardized, bulletproof execution system.&lt;/p&gt;




&lt;p&gt;[ignore keywords: "Why use Expo EAS build", "Expo EAS vs manual Xcode archive", "Benefits of Expo OTA updates", "React Native cloud build pipeline", "Is Expo EAS worth it for solo developers"]&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>expo</category>
      <category>eas</category>
      <category>mobile</category>
    </item>
    <item>
      <title>The Bulletproof Guide to Setting Up Expo EAS &amp; OTA Updates (Without the Tears)</title>
      <dc:creator>Debojit Das</dc:creator>
      <pubDate>Tue, 16 Jun 2026 08:26:17 +0000</pubDate>
      <link>https://dev.to/debojitdas2003/the-bulletproof-guide-to-setting-up-expo-eas-ota-updates-without-the-tears-4le3</link>
      <guid>https://dev.to/debojitdas2003/the-bulletproof-guide-to-setting-up-expo-eas-ota-updates-without-the-tears-4le3</guid>
      <description>&lt;p&gt;Setting up Expo Application Services (EAS) and Over-The-Air (OTA) updates for the first time feels like unlocking a developer superpower. The promise is incredible: ship bug fixes directly to your users' phones instantly, completely bypassing the App Store review process.&lt;/p&gt;

&lt;p&gt;But if you set it up incorrectly, that superpower quickly turns into a nightmare of instant crashes, poisoned update channels, and the dreaded White Screen of Death (WSOD).&lt;/p&gt;

&lt;p&gt;I recently navigated this minefield while building a production React Native app using Supabase and RevenueCat. I learned the hard way that the gap between local development and cloud builds is filled with hidden traps.&lt;/p&gt;

&lt;p&gt;If you are setting up EAS and &lt;code&gt;expo-updates&lt;/code&gt; from scratch, this step-by-step guide will ensure you get it right on the very first try.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Master Your Environment Variables
&lt;/h2&gt;

&lt;p&gt;This is where 90% of developers fail on their first EAS build. In local development, your app magically reads your &lt;code&gt;.env&lt;/code&gt; file. In the cloud, your &lt;code&gt;.env&lt;/code&gt; file is ignored (thanks to &lt;code&gt;.gitignore&lt;/code&gt;), leaving your API keys blank and crashing your app on launch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Golden Rules of Expo Environments:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Prefix:&lt;/strong&gt; Any API key that your frontend React Native code needs to read (like your Supabase URL or RevenueCat Public Key) &lt;strong&gt;must&lt;/strong&gt; start with &lt;code&gt;EXPO_PUBLIC_&lt;/code&gt;. If it doesn't, the Metro bundler will actively strip it out of your production build for security.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Local File:&lt;/strong&gt; Keep a single &lt;code&gt;.env.local&lt;/code&gt; file on your machine for development.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your &lt;code&gt;.env.local&lt;/code&gt; should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;EXPO_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
EXPO_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
EXPO_PUBLIC_REVENUECAT_IOS_API_KEY=appl_your_key

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Upload to the Cloud:&lt;/strong&gt;&lt;br&gt;
Before you ever trigger a build, you must upload these exact variables to the EAS servers so they can be injected into your production app. Run these commands in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;eas &lt;span class="nb"&gt;env&lt;/span&gt;:create &lt;span class="nt"&gt;--name&lt;/span&gt; EXPO_PUBLIC_SUPABASE_URL &lt;span class="nt"&gt;--value&lt;/span&gt; &lt;span class="s2"&gt;"https://your-project.supabase.co"&lt;/span&gt; &lt;span class="nt"&gt;--environment&lt;/span&gt; production &lt;span class="nt"&gt;--visibility&lt;/span&gt; plaintext
eas &lt;span class="nb"&gt;env&lt;/span&gt;:create &lt;span class="nt"&gt;--name&lt;/span&gt; EXPO_PUBLIC_SUPABASE_ANON_KEY &lt;span class="nt"&gt;--value&lt;/span&gt; &lt;span class="s2"&gt;"your-anon-key"&lt;/span&gt; &lt;span class="nt"&gt;--environment&lt;/span&gt; production &lt;span class="nt"&gt;--visibility&lt;/span&gt; plaintext

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check your work by running &lt;code&gt;eas env:list --environment production&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Bulletproof Your &lt;code&gt;app.json&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;EAS runs strict validation checks on your project manifest. If you have sloppy configuration, your OTA updates will be rejected at the 99% mark.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Keep it DRY (Don't Repeat Yourself):&lt;/strong&gt; Scroll through your &lt;code&gt;app.json&lt;/code&gt;. Ensure you do not have duplicate entries in your iOS &lt;code&gt;UIBackgroundModes&lt;/code&gt;, Android &lt;code&gt;permissions&lt;/code&gt;, or &lt;code&gt;intentFilters&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lock in the Runtime Version:&lt;/strong&gt; OTA updates need to know which native binary they belong to. Add a strict runtime version to your &lt;code&gt;app.json&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MyApp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"runtimeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"updates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://u.expo.dev/YOUR-PROJECT-ID"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 3: Configure &lt;code&gt;eas.json&lt;/code&gt; Correctly
&lt;/h2&gt;

&lt;p&gt;When you initialize EAS (via &lt;code&gt;eas init&lt;/code&gt;), it generates an &lt;code&gt;eas.json&lt;/code&gt; file. You need to explicitly tell your production profile to pull down the cloud environment variables you set up in Step 1, and map the build to an update channel.&lt;/p&gt;

&lt;p&gt;Edit your &lt;code&gt;eas.json&lt;/code&gt; to look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"production"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"channel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"production"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"environment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"production"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note: The &lt;code&gt;"environment": "production"&lt;/code&gt; line is the magic link that pulls your &lt;code&gt;eas env&lt;/code&gt; keys into the build.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: The Local Safety Check
&lt;/h2&gt;

&lt;p&gt;Do not skip this step. Waiting 30 minutes in the EAS cloud queue just to discover your app crashes on launch is a brutal loop.&lt;/p&gt;

&lt;p&gt;Because you are likely using custom native code, you can compile and test the true production release right on your local machine.&lt;/p&gt;

&lt;p&gt;Run this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx expo run:ios &lt;span class="nt"&gt;--configuration&lt;/span&gt; Release

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(Or &lt;code&gt;run:android --variant Release&lt;/code&gt;)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If the app boots up on your simulator, connects to your database, and doesn't white-screen, your JavaScript and native configurations are rock solid.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: The First Cloud Build
&lt;/h2&gt;

&lt;p&gt;Now you are ready for the cloud. Trigger your first production build for TestFlight or the Google Play Console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;eas build &lt;span class="nt"&gt;--profile&lt;/span&gt; production &lt;span class="nt"&gt;--platform&lt;/span&gt; all

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;A Warning About Local Builds:&lt;/strong&gt; If you try to save queue time by running &lt;code&gt;eas build --local&lt;/code&gt;, the EAS CLI will ignore your local &lt;code&gt;.env&lt;/code&gt; and will &lt;em&gt;not&lt;/em&gt; reach out to the cloud to get your &lt;code&gt;eas env&lt;/code&gt; variables. It will build a blank app. Always use the cloud build for your first clean run, or run &lt;code&gt;eas env:pull&lt;/code&gt; before attempting a local EAS build.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 6: Pushing Your First OTA Update Safely
&lt;/h2&gt;

&lt;p&gt;Once your app is live on TestFlight or the App Store, you will eventually want to push a bug fix over the air.&lt;/p&gt;

&lt;p&gt;Unlike &lt;code&gt;eas build&lt;/code&gt; (which happens in the cloud), &lt;code&gt;eas update&lt;/code&gt; bundles your JavaScript &lt;strong&gt;locally on your computer&lt;/strong&gt; before uploading it. This means it relies entirely on your local terminal's environment variables.&lt;/p&gt;

&lt;p&gt;If your Metro bundler has stale cache, it might bundle your app without your &lt;code&gt;EXPO_PUBLIC_&lt;/code&gt; keys, resulting in a "poisoned" update that instantly crashes your users' apps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Golden Command for OTA Updates:&lt;/strong&gt;&lt;br&gt;
Always force Metro to clear its cache when pushing to production so it strictly reads your &lt;code&gt;.env.local&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;eas update &lt;span class="nt"&gt;--branch&lt;/span&gt; production &lt;span class="nt"&gt;--clear-cache&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Provide an update message when prompted, and within seconds, your users will download the fresh, bug-free JavaScript bundle the next time they open the app.&lt;/p&gt;




&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;By moving your client keys to &lt;code&gt;eas env&lt;/code&gt;, cleaning up your &lt;code&gt;app.json&lt;/code&gt;, and respecting the Metro cache during &lt;code&gt;eas update&lt;/code&gt;, you completely eliminate the friction of modern app deployment. You can now build, iterate, and ship OTA updates with total confidence.&lt;/p&gt;




&lt;p&gt;[ignore keywords:&lt;br&gt;
"How to set up Expo EAS from scratch"&lt;/p&gt;

&lt;p&gt;"Expo OTA updates complete beginner guide"&lt;/p&gt;

&lt;p&gt;"How to use environment variables in EAS build"&lt;/p&gt;

&lt;p&gt;"Expo runtime version best practices"&lt;/p&gt;

&lt;p&gt;"EAS build local vs cloud environment variables"&lt;/p&gt;

&lt;p&gt;"How to trigger safe expo updates"&lt;/p&gt;

&lt;p&gt;"React Native build production ipa and aab"&lt;/p&gt;

&lt;p&gt;"Preventing poisoned OTA updates in Expo"&lt;br&gt;
]&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>expo</category>
      <category>eas</category>
      <category>ota</category>
    </item>
    <item>
      <title>How to Fix React Native EAS Build White Screens and OTA Crashes (A Complete Guide)</title>
      <dc:creator>Debojit Das</dc:creator>
      <pubDate>Tue, 16 Jun 2026 07:47:55 +0000</pubDate>
      <link>https://dev.to/debojitdas2003/how-to-fix-react-native-eas-build-white-screens-and-ota-crashes-a-complete-guide-2e9n</link>
      <guid>https://dev.to/debojitdas2003/how-to-fix-react-native-eas-build-white-screens-and-ota-crashes-a-complete-guide-2e9n</guid>
      <description>&lt;p&gt;If you are transitioning your React Native or Expo app from traditional local builds to Expo Application Services (EAS) and Over-The-Air (OTA) updates, you might feel like you just gained a superpower.&lt;/p&gt;

&lt;p&gt;That is, until you download your app from TestFlight and are greeted by a completely blank white screen. Or worse, the app instantly crashes before it even loads.&lt;/p&gt;

&lt;p&gt;I recently migrated an app to EAS and ran into these exact issues. In development, everything worked perfectly. In production, everything fell apart. After hours of parsing system logs, I realized the problems weren't with my code, but with how EAS handles environment variables and OTA updates.&lt;/p&gt;

&lt;p&gt;If your Expo app is white-screening or crashing in production, here is the complete, beginner-friendly guide to fixing it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 1: The White Screen of Death (WSOD)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Symptom
&lt;/h3&gt;

&lt;p&gt;Your app works flawlessly on your local simulator. You build it for iOS or Android, install it on a physical device, and it hangs forever on a blank white screen.&lt;/p&gt;

&lt;p&gt;If you plug your phone into your computer and check the device console, you will likely see a fatal JavaScript error that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Unhandled&lt;/span&gt; &lt;span class="nx"&gt;JS&lt;/span&gt; &lt;span class="nx"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt; &lt;span class="nx"&gt;not&lt;/span&gt; &lt;span class="nx"&gt;ready&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;EXPO_PUBLIC_SUPABASE_URL&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="nx"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;not&lt;/span&gt; &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nx"&gt;or&lt;/span&gt; &lt;span class="nx"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;js&lt;/span&gt; &lt;span class="nx"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;hermes&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt; &lt;span class="nx"&gt;not&lt;/span&gt; &lt;span class="nx"&gt;ready&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;Invariant&lt;/span&gt; &lt;span class="nx"&gt;Violation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;main&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;has&lt;/span&gt; &lt;span class="nx"&gt;not&lt;/span&gt; &lt;span class="nx"&gt;been&lt;/span&gt; &lt;span class="nx"&gt;registered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;This&lt;/span&gt; &lt;span class="nx"&gt;can&lt;/span&gt; &lt;span class="nx"&gt;happen&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nc"&gt;Metro &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt; &lt;span class="nx"&gt;dev&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;wrong&lt;/span&gt; &lt;span class="nx"&gt;folder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;A&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="nx"&gt;failed&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;load&lt;/span&gt; &lt;span class="nx"&gt;due&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;an&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="s2"&gt;`AppRegistry.registerComponent`&lt;/span&gt; &lt;span class="nx"&gt;wasn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;t called.

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Root Cause: The Environment Variable Trap
&lt;/h3&gt;

&lt;p&gt;When you run a local development server, your bundler easily reads your &lt;code&gt;.env&lt;/code&gt; file to get your API keys (like your Supabase URL).&lt;/p&gt;

&lt;p&gt;But when you build your app for production—whether on Expo's cloud servers or using a local EAS build—your &lt;code&gt;.env&lt;/code&gt; file is left behind because it is in your &lt;code&gt;.gitignore&lt;/code&gt;. If your app tries to initialize a database connection without those keys, the React tree silently crashes before rendering the UI.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Fix: Properly Exposing Your Keys
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Use the &lt;code&gt;EXPO_PUBLIC_&lt;/code&gt; Prefix&lt;/strong&gt;&lt;br&gt;
EAS has a strict safety mechanism: it will ignore any environment variable that does not start with &lt;code&gt;EXPO_PUBLIC_&lt;/code&gt;. This prevents you from accidentally shipping secret admin keys into your public app.&lt;br&gt;
Only client-side APIs should have this prefix.&lt;/p&gt;

&lt;p&gt;Change your code and your environment files to look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;EXPO_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
EXPO_PUBLIC_SUPABASE_ANON_KEY=your-anon-key

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: Upload Environment Variables to EAS&lt;/strong&gt;&lt;br&gt;
If you are using Expo's cloud to build your app, you need to securely pass these variables to their servers. Open your terminal and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;eas &lt;span class="nb"&gt;env&lt;/span&gt;:create &lt;span class="nt"&gt;--name&lt;/span&gt; EXPO_PUBLIC_SUPABASE_URL &lt;span class="nt"&gt;--value&lt;/span&gt; &lt;span class="s2"&gt;"your_prod_url"&lt;/span&gt; &lt;span class="nt"&gt;--environment&lt;/span&gt; production &lt;span class="nt"&gt;--visibility&lt;/span&gt; plaintext

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(Repeat this for every client-side variable your app needs).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Once done, verify they were uploaded properly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;eas &lt;span class="nb"&gt;env&lt;/span&gt;:list &lt;span class="nt"&gt;--environment&lt;/span&gt; production

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3: Link the Environment in `eas.json&lt;/strong&gt;&lt;code&gt;&lt;br&gt;
You must explicitly tell your build profile to pull down the production variables you just uploaded. Check your &lt;/code&gt;eas.json&lt;code&gt; thoroughly. The &lt;/code&gt;"environment": "production"` key must exist inside your build object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"production"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"autoIncrement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"environment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"production"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Part 2: The Local Build Caveat (&lt;code&gt;--local&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;To save on cloud computing costs and avoid queue times, many developers build their &lt;code&gt;.ipa&lt;/code&gt; or &lt;code&gt;.aab&lt;/code&gt; files locally using the &lt;code&gt;--local&lt;/code&gt; flag.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Trap:&lt;/strong&gt; If you run &lt;code&gt;eas build --platform ios --profile production --local&lt;/code&gt;, EAS isolates your project in a temporary folder. It still ignores your &lt;code&gt;.env&lt;/code&gt; file, and because it is offline, it &lt;em&gt;does not&lt;/em&gt; fetch the variables you uploaded to the cloud in Step 2. You will end up with another white screen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Fix:&lt;/strong&gt; Before building locally, you need to create an &lt;code&gt;.env.local&lt;/code&gt; file in your root directory.&lt;/p&gt;

&lt;p&gt;You can either manually copy-paste your variables into it, or—especially if you are collaborating with others—pull them directly from the cloud:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;eas &lt;span class="nb"&gt;env&lt;/span&gt;:pull &lt;span class="nt"&gt;--environment&lt;/span&gt; production

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before you waste time building the full &lt;code&gt;.ipa&lt;/code&gt;, test your production environment locally on your simulator to ensure no white screens appear:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx expo run:ios &lt;span class="nt"&gt;--configuration&lt;/span&gt; Release

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once that works, you can safely run your local build:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;eas build &lt;span class="nt"&gt;--platform&lt;/span&gt; ios &lt;span class="nt"&gt;--profile&lt;/span&gt; production &lt;span class="nt"&gt;--local&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Part 3: Instant Crashes and Poisoned OTA Updates
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Symptom
&lt;/h3&gt;

&lt;p&gt;You successfully fix the white screen, but after pushing an Over-The-Air (OTA) update using &lt;code&gt;eas update&lt;/code&gt;, TestFlight users report the app instantly crashes the millisecond it opens.&lt;/p&gt;

&lt;p&gt;If you look at the logs, you might see Apple's system daemon logging a user force-quit after a freeze:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ExceptionCode: "User Initiated Quit (0xDEADFA11)"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, when trying to push the update, you get a rejection from Expo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Manifest Validation Error:
android/intentFilters:must NOT have duplicate items (items ## 0 and 1 are identical)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Root Cause: Poisoned Bundles &amp;amp; Dirty Manifests
&lt;/h3&gt;

&lt;p&gt;Unlike &lt;code&gt;eas build&lt;/code&gt; (which uses the cloud environment), &lt;code&gt;eas update&lt;/code&gt; bundles your JavaScript entirely on your local machine. If your local terminal doesn't have the &lt;code&gt;EXPO_PUBLIC_&lt;/code&gt; variables loaded, you will push an update with blank API keys.&lt;/p&gt;

&lt;p&gt;Because your &lt;code&gt;app.json&lt;/code&gt; has a set &lt;code&gt;"runtimeVersion"&lt;/code&gt;, every user who opens the app immediately downloads this broken update, overwriting your good code and bricking the app. You have "poisoned" the update channel.&lt;/p&gt;

&lt;p&gt;Furthermore, EAS runs strict validation on your &lt;code&gt;app.json&lt;/code&gt; during updates. If you have duplicate iOS background modes or Android permissions, the update will fail.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Fix
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Clean up `app.json&lt;/strong&gt;&lt;code&gt;&lt;br&gt;
Ensure you have absolutely no duplicate entries in your &lt;/code&gt;intentFilters&lt;code&gt;, &lt;/code&gt;permissions&lt;code&gt;, or &lt;/code&gt;UIBackgroundModes`.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Bump the Runtime Version&lt;/strong&gt;&lt;br&gt;
To escape the poisoned OTA loop, simply sever the link. Open &lt;code&gt;app.json&lt;/code&gt; and change your runtime version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"runtimeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.1"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Clear Cache and Update&lt;/strong&gt;&lt;br&gt;
Ensure your &lt;code&gt;.env.local&lt;/code&gt; file is populated. Then, force Metro to clear its cache and push a clean update to the new runtime version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;eas update &lt;span class="nt"&gt;--branch&lt;/span&gt; production &lt;span class="nt"&gt;--clear-cache&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, build a fresh &lt;code&gt;.ipa&lt;/code&gt; or &lt;code&gt;.aab&lt;/code&gt;. Because the runtime version changed, it will ignore the old, broken OTA update and boot normally.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Ultimate Summary Checklist
&lt;/h2&gt;

&lt;p&gt;To never face these issues again, follow this exact order of operations when setting up EAS and OTA updates:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Prefix Client APIs:&lt;/strong&gt; Ensure all front-end keys start with &lt;code&gt;EXPO_PUBLIC_&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloud Sync:&lt;/strong&gt; Upload them to Expo via &lt;code&gt;eas env:create&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local Sync:&lt;/strong&gt; Run &lt;code&gt;eas env:pull --environment production&lt;/code&gt; to generate your &lt;code&gt;.env.local&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configure &lt;code&gt;eas.json&lt;/code&gt;:&lt;/strong&gt; Ensure &lt;code&gt;"environment": "production"&lt;/code&gt; is set in your production build profile.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clean Manifest:&lt;/strong&gt; Check &lt;code&gt;app.json&lt;/code&gt; for any duplicate arrays or permissions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simulate Production:&lt;/strong&gt; Always run &lt;code&gt;npx expo run:ios --configuration Release&lt;/code&gt; locally first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build:&lt;/strong&gt; Run &lt;code&gt;eas build --profile production&lt;/code&gt; (add &lt;code&gt;--local&lt;/code&gt; if you want to use your own machine's hardware).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Update:&lt;/strong&gt; When pushing OTA fixes, always use &lt;code&gt;eas update --branch production --clear-cache&lt;/code&gt; to prevent stale, broken bundles from deploying.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;[ignore keywords:&lt;br&gt;
"React Native TestFlight white screen of death"&lt;/p&gt;

&lt;p&gt;"Unhandled JS Exception: [runtime not ready]"&lt;/p&gt;

&lt;p&gt;"EXPO_PUBLIC_SUPABASE_URL environment variable is not set"&lt;/p&gt;

&lt;p&gt;"Expo app working on simulator but crashing on TestFlight"&lt;/p&gt;

&lt;p&gt;"EAS build local env file missing"&lt;/p&gt;

&lt;p&gt;"ExceptionCode: User Initiated Quit (0xDEADFA11)"&lt;/p&gt;

&lt;p&gt;"Invariant Violation: main has not been registered expo"&lt;/p&gt;

&lt;p&gt;"Manifest Validation Error: android/intentFilters duplicate items"&lt;br&gt;
]&lt;/p&gt;

</description>
      <category>expo</category>
      <category>reactnative</category>
      <category>eas</category>
      <category>mobile</category>
    </item>
  </channel>
</rss>
