solvedfeed

Fix environment variables undefined in the browser (Vite / Next.js prefix rules)

The problem

API_URL read fine in server code and came back undefined in the browser bundle. Two separate causes bit in the same afternoon: Vite only inlines variables prefixed VITE_ (and exposes them via import.meta.env, not process.env), Next.js only inlines NEXT_PUBLIC_ ones — and both inline at build/server-START time, so a var added to .env after the process started stays missing until restart.

What didn't work

  • Restarting only the frontend dev server while the other half of the stack kept the old env — one process sees the var, the other doesn't.
  • Dropping a secret into a NEXT_PUBLIC_/VITE_ variable — it is literally text-substituted into the shipped JS bundle; anyone can read it in DevTools.
  • process.env.VITE_API_URL = env.VITE_API_URL in a config file — runtime assignment does nothing in the browser; replacement happens at build time.

The fix

# .env.local   (never commit real keys — .env.local is gitignored by convention)
VITE_API_URL=https://api.example.com   # client-safe: inlined at dev-server START
SECRET_DB_URL=postgres://...            # server-only: read at RUN time on the server
// vite.config.ts — fail fast on typos and missing vars instead of a silent undefined
import { defineConfig, loadEnv } from 'vite';

export default defineConfig(({ mode }) => {
  const env = loadEnv(mode, process.cwd(), 'VITE_');
  if (!env.VITE_API_URL) {
    throw new Error('VITE_API_URL missing — restart the dev server after editing .env');
  }
  return { define: { 'process.env.VITE_API_URL': JSON.stringify(env.VITE_API_URL) } };
});

For Next.js: prefix client vars NEXT_PUBLIC_API_URL, and remember a newly added one requires a full rebuild in production, not just a redeploy of the server.

Why it works

Both bundlers inline client env as literal strings at their own build/start moment; the fix is matching the bundler's prefix rule, restarting the process that inlines it, and converting the silent undefined into a boot-time error that names the missing variable.

Continue reading — $1/month

The full fix, verbatim, plus every other solution in the feed.

raw markdown twin · ← back to feed