◉RORK LABJP
●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SDK 58 — Expo SDK 58 is out of beta as of 10/15 (beta since 09/15). Check your dependencies before upgrading●WIDGET — A write-up traces an iOS widget that renders black only in production builds●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SDK 58 — Expo SDK 58 is out of beta as of 10/15 (beta since 09/15). Check your dependencies before upgrading●WIDGET — A write-up traces an iOS widget that renders black only in production builds●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…
Articles/Dev Tools
⬡ Dev Tools/2026-09-04Advanced

EAS secret visibility does not keep a value out of your app — deciding prefix and visibility separately

The EXPO_PUBLIC_ prefix decides what ships inside your app; EAS visibility decides who can read it. Why stacking them blanks a value on OTA updates, and how to check your build.

Expo221EAS9environment variables5React Native240Rork591

✦ Premium Article

I was running grep over a freshly exported JavaScript bundle during a pre-release check. What I was looking for was an analytics key I had prefixed with EXPO_PUBLIC_ myself.

It was there. That was expected, and still slightly unsettling, because I had set that variable's visibility on EAS to the most restricted level and had spent a while assuming that restricting it there also restricted what ended up inside the artifact.

The two settings decide entirely different things. The prefix decides whether a value is embedded in the shipped artifact. Visibility decides who is allowed to read it. Prefix governs the artifact, visibility governs the path. Since I started holding those apart, deciding where a key belongs stopped taking any real time.

The prefix and the visibility answer different questions

The first thing that stops adding up in Expo environment variables is treating these as one sliding "security level". Side by side, the mismatch is obvious.

AxisWhat it decidesHow it takes effect
The EXPO_PUBLIC_ prefixWhether the value is embedded in client codeAt bundle time, process.env.EXPO_PUBLIC_* is replaced inline with the value
EAS visibility (plaintext / sensitive / secret)Who can read the value, and whereChanges what appears in the dashboard, in EAS CLI, and in job logs

So a variable set to secret still lands in the artifact if its name begins with EXPO_PUBLIC_ and client code reads it. Expo's own documentation says as much: on Environment variables in EAS, secrets "do not provide any additional security for values that you end up embedding in your application itself".

I had read that line before. I did not miss the words so much as get pulled along by the feel of the word "secret".

What secret actually stops is everything outside EAS

Secret visibility works on the paths that carry a value off EAS servers. It helps to name what closes and what stays open.

What closes: display in the dashboard, reads from EAS CLI, values printed into job logs, and eas env:pull writing the value down into a local .env file.

What stays open: the build job itself. Builds run on EAS servers, so a secret value is available there, and the bundler embeds it into client code exactly as it would any other value. Once embedded, it is part of what you ship.

"I don't want anyone reading this" and "I don't want this inside the app" sound alike and are different requirements. The first is answered by visibility. The second is answered by the prefix and by your architecture.

✦

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
✦You will be able to separate the two axes — prefix and visibility — and decide where each key in your own app belongs without second-guessing
✦You will be able to catch, before release, the failure where a store build works fine but an OTA update ships with a blank value, which is painful to diagnose afterwards
✦You will be able to drop a bundle-search step and an EAS variable audit script straight into your own pre-ship checklist
Secure payment via Stripe · Cancel anytime
✦

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

Related Articles

⬡ Dev Tools2026-09-06
There is no --json on eas env:list, so I rebuilt my environment variable inventory
eas env:list does not accept a --json flag. Here is how I turn --format short output into safe JSON, and how I spot a missing variable across three environments in one line.
⬡ Dev Tools2026-08-22
Every bulk replace exited zero. The damage was in the lines I did not delete
Run a bulk replace over generated code and the breakage lands on the neighbouring lines, not the matched ones. Here is what broke in a live project, and a dependency-free guard that checks the invariants a replace must preserve.
⬡ Dev Tools2026-07-30
What Renovate may bump in an Expo app, and what it must never touch
Turning on automated dependency updates in a Rork-generated app also hands Renovate the 123 packages Expo SDK 57 pins. Measured on 2026-07-30, six of them sit a full major version behind npm latest. Here is how to generate the ignore list from the SDK instead of maintaining it by hand.