DEV Community

Cathy Lai
Cathy Lai

Posted on

Supabase config in your Expo project - the proper way

If you’ve ever shipped an update with Expo and suddenly hit this dreaded runtime error:

Error: supabaseUrl is required., js engine: hermes
Enter fullscreen mode Exit fullscreen mode

…you’re not alone. This happens when your environment variables don’t make it into the runtime bundle. With Expo’s OTA updates (eas update), only JavaScript and assets get updated — no new native code. If your Supabase keys aren’t baked into the binary, they’ll resolve as undefined.

The fix is simple: move your Supabase config into Expo’s extra config via app.config.ts.


Why app.json fails with env variables

By default, Expo projects ship with an app.json. It works great for static values, but JSON doesn’t understand JavaScript or process.env.

Example ❌ (this will break):

{
  "expo": {
    "name": "my-app",
    "slug": "my-app",
    "extra": {
      "supabaseUrl": process.env.SUPABASE_URL,
      "supabaseAnonKey": process.env.SUPABASE_ANON_KEY
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

That throws SyntaxError: invalid character 'p' because process.env is not valid JSON.


Step 1 — Use app.config.ts instead

Rename your config file:

mv app.json app.config.ts
Enter fullscreen mode Exit fullscreen mode

Then set it up like this:

import 'dotenv/config';

export default {
  expo: {
    name: "my-app",
    slug: "my-app",
    version: "1.0.0",
    runtimeVersion: { policy: "sdkVersion" },
    extra: {
      supabaseUrl: process.env.SUPABASE_URL,
      supabaseAnonKey: process.env.SUPABASE_ANON_KEY,
      unsplashKey: process.env.UNSPLASH_KEY,
    },
    owner: "your-expo-username"
  }
};
Enter fullscreen mode Exit fullscreen mode

Now Expo will pull in your .env values and bake them into the app.


Step 2 — Expose them in your code

Create or update your env.ts:

import Constants from 'expo-constants';

const extra = Constants.expoConfig?.extra || {};

export const CONFIG = {
  supabaseUrl: extra.supabaseUrl as string,
  supabaseAnonKey: extra.supabaseAnonKey as string,
  unsplashKey: extra.unsplashKey as string,
};
Enter fullscreen mode Exit fullscreen mode

This way, your Supabase client always gets defined values:

import { createClient } from '@supabase/supabase-js';
import { CONFIG } from './env';

export const supabase = createClient(CONFIG.supabaseUrl, CONFIG.supabaseAnonKey);
Enter fullscreen mode Exit fullscreen mode

Step 3 — Add secrets to EAS

When you build or submit your app, Expo’s cloud build environment needs access to those same values. You don’t want to commit .env into GitHub. Instead, use EAS secrets:

eas secret:create --name SUPABASE_URL --value https://xxxx.supabase.co
eas secret:create --name SUPABASE_ANON_KEY --value your-anon-key
eas secret:create --name UNSPLASH_KEY --value your-unsplash-key
Enter fullscreen mode Exit fullscreen mode

On build, Expo will inject them into process.env, and app.config.ts will pick them up.


Step 4 — Rebuild once, then enjoy OTA updates

The key detail: OTA updates can’t add new env variables. You need one full native build that includes your Supabase keys. After that, all future eas update commands can safely push new JS/asset changes without breaking Supabase.


Final Thoughts

If you rely on Supabase (or any API key) in Expo apps:

  • ❌ Don’t put process.env directly in app.json.
  • ✅ Do move your config into app.config.ts with extra.
  • ✅ Use expo-constants in your app to read them.
  • ✅ Store keys securely in EAS secrets.

That small change will save you hours of debugging the next time you ship an OTA update. 🚀

Top comments (0)