DEV Community

Cover image for Fix "supabaseUrl is required" in Supabase JS
Mahdi BEN RHOUMA
Mahdi BEN RHOUMA

Posted on Originally published at iloveblogs.blog

Fix "supabaseUrl is required" in Supabase JS

TL;DR

createClient(supabaseUrl, supabaseKey) throws Error: supabaseUrl is required the moment supabaseUrl is an empty string, undefined, or whitespace — it is a synchronous check inside supabase-js, not a network error. In nearly every report the URL variable itself is fine in .env; it just never reached process.env (or import.meta.env) in the running app because of a missing prefix, a misnamed file, or a dev server that was never restarted after the file changed. Fix the naming convention for your build tool, confirm the filename starts with a dot, restart the dev server, and log the value right before createClient() to prove it is no longer empty.

Uncaught Error: supabaseUrl is required.
Enter fullscreen mode Exit fullscreen mode

Why it happens

supabase-js validates its two required constructor arguments before it does anything else. In the current source, SupabaseClient's constructor calls validateSupabaseUrl(supabaseUrl), and that helper throws immediately if the trimmed URL is falsy:

export function validateSupabaseUrl(supabaseUrl: string): URL {
  const trimmedUrl = supabaseUrl?.trim()
  if (!trimmedUrl) {
    throw new Error('supabaseUrl is required.')
  }
  // ...
}
Enter fullscreen mode Exit fullscreen mode

Right after that check, the constructor throws a matching supabaseKey is required. if the second argument is empty. Both checks run before any HTTP request is made — the error has nothing to do with your Supabase project, your network, or Postgres itself. It is telling you that the value passed into createClient() was "", null, or undefined at the moment the module executed (source: supabase-js SupabaseClient.ts constructor and validateSupabaseUrl in helpers.ts).

That means the bug is almost always upstream of Supabase: the environment variable that should have held the URL never made it into process.env (or import.meta.env) at the point the client file ran. Client-side bundlers only expose a subset of your .env values to browser code, and each one uses a different rule:

  • Create React App: only variables prefixed REACT_APP_ are embedded, and only at build time — "changing any environment variables will require you to restart the development server" (CRA docs).
  • Vite: only variables prefixed VITE_ are exposed, read via import.meta.env.VITE_... instead of process.env, and .env files "are loaded at the start of Vite" — a restart is required after editing them (Vite docs).
  • Next.js: only variables prefixed NEXT_PUBLIC_ are inlined into client bundles; everything else stays server-only, and the replacement happens at next build / dev-server-start time (Next.js docs).

If the code calling createClient() reads process.env.REACT_APP_SUPABASE_URL but the app is actually bundled with Vite, or the .env file was saved after the dev server started, the variable is undefined in the running process even though it looks correct on disk. supabase-js then does exactly what it is supposed to do: refuse to construct a client with no URL.

Fix

1. Match the variable name in code to the variable name in the file, exactly.

The most common cause reported in this exact error (Stack Overflow, 68239168) is a mismatch between the name read in code and the name written in .env. Confirm both sides use the identical string, including case:

# .env (Create React App)
REACT_APP_SUPABASE_URL=https://your-project-ref.supabase.co
REACT_APP_SUPABASE_ANON_KEY=your-anon-key
Enter fullscreen mode Exit fullscreen mode
// supabaseClient.js
const supabaseUrl = process.env.REACT_APP_SUPABASE_URL
const supabaseAnonKey = process.env.REACT_APP_SUPABASE_ANON_KEY
Enter fullscreen mode Exit fullscreen mode

2. Check the filename has a leading dot.

A file saved as env.local (no dot) is invisible to Create React App, Vite, and Next.js — none of them look for that filename. It must be .env, .env.local, .env.development, or the equivalent your framework documents. Rename it if the leading dot is missing:

mv env.local .env.local
Enter fullscreen mode Exit fullscreen mode

3. Use the prefix your bundler actually requires.

If the project runs on Vite, REACT_APP_ variables are silently ignored — rename them and switch to import.meta.env:

# .env (Vite)
VITE_SUPABASE_URL=https://your-project-ref.supabase.co
VITE_SUPABASE_ANON_KEY=your-anon-key
Enter fullscreen mode Exit fullscreen mode
// supabaseClient.js
import { createClient } from '@supabase/supabase-js'

const supabaseUrl = import.meta.env.VITE_SUPABASE_URL
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY

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

On Next.js, use NEXT_PUBLIC_ for anything read in client components, and keep it as a plain server-only name (no prefix) for values only used inside Server Components or Route Handlers:

# .env.local (Next.js)
NEXT_PUBLIC_SUPABASE_URL=https://your-project-ref.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
Enter fullscreen mode Exit fullscreen mode

4. Restart the dev server after touching any .env file.

All three tools load .env files once, when the process starts. Stop the dev server completely and start it again — a browser refresh alone reuses the already-loaded values:

# stop the running dev server (Ctrl+C), then
npm run dev
Enter fullscreen mode Exit fullscreen mode

5. Prove the value is actually set before calling createClient().

Add a temporary log immediately above the createClient() call. If this prints undefined, the problem is still in step 1–4, not in Supabase:

console.log('supabaseUrl:', supabaseUrl)
export const supabase = createClient(supabaseUrl, supabaseAnonKey)
Enter fullscreen mode Exit fullscreen mode

Remove the log once the value prints a real URL instead of undefined.

Verify the fix

Reload the app and confirm three things: the console log from step 5 prints the full https://your-project-ref.supabase.co URL, the Error: supabaseUrl is required no longer appears, and a real Supabase call (e.g. supabase.auth.getSession() or a .select() on a table) resolves instead of throwing. If the error is gone but requests now fail with a 401 or an invalid API key error, the URL loaded correctly but the anon key did not — repeat the same prefix/filename/restart check for the key variable.

Common variants

Deploying the same app to Vercel, Netlify, or another host reintroduces this error even after it is fixed locally, because .env files are gitignored by default and never uploaded — the platform's own environment variable dashboard has to have the same NEXT_PUBLIC_/REACT_APP_/VITE_-prefixed keys set, and most platforms require a redeploy (not just a restart) to re-inline them into the build. A second variant shows up in monorepos or Docker builds where the .env file lives at the repository root but the build context only copies a subpackage directory — the file is never present at build time even though it exists in source control. A third variant is calling createClient() at module scope in a file that gets imported before a framework's env-loading step runs (for example, a config file imported ahead of Next.js's own environment bootstrapping); moving the createClient() call inside a function, or lazily instantiating the client on first use, avoids constructing it before the variable exists.

None of these variants change what supabase-js is checking — a non-empty string for both arguments — only where the empty string is coming from. Treat every "supabaseUrl is required" report the same way regardless of framework: stop assuming Supabase or the network is broken, and instead trace the exact value the client receives back to the .env file, the prefix rule, and whether the process that read it has been restarted since the file last changed. Once that value prints correctly in a plain console.log, the error resolves itself because there is nothing left for supabase-js to reject. For related Supabase connection issues once the client actually initializes, see the Docker/Postgres ECONNREFUSED fix, the Prisma DATABASE_URL not found troubleshooting guide, the breakdown of why Supabase queries run slow, and the Supabase "Database Error Saving New User" trigger fix.


Originally published at https://www.iloveblogs.blog

Top comments (0)