GuidesMay 15, 20269 min read

Vercel Environment Variables for Next.js: Complete Guide (2026)

Everything you need to know about environment variables in Next.js on Vercel — adding via CLI vs dashboard, NEXT_PUBLIC_ prefix, build-time vs runtime, secrets vs preview vs production, and the trailing-newline gotcha.

The short answer: Add environment variables to Vercel via the dashboard (Settings → Environment Variables) or the CLI (vercel env add NAME). Server-only vars stay private; NEXT_PUBLIC_-prefixed vars get baked into the client bundle at build time. Different values for production / preview / development. Watch for the trailing-newline gotcha if you pipe values in.

The three ways to add env vars

1. Dashboard (easiest for one-offs)

Vercel dashboard → your project → Settings → Environment Variables. Click "Add New", paste the key and value, check which environments (Production / Preview / Development), click Save. The variable is encrypted at rest immediately. Next deploy picks it up.

2. CLI (fastest for setup)

# Add to all environments interactively
vercel env add OPENAI_API_KEY

# Or scope to one environment
vercel env add OPENAI_API_KEY production
vercel env add OPENAI_API_KEY preview
vercel env add OPENAI_API_KEY development

# List what's set
vercel env ls production

# Remove
vercel env rm OPENAI_API_KEY production

# Pull all production env vars into local .env.local
vercel env pull .env.local

The CLI is the right tool when you're bootstrapping a new project or copying env vars between projects. The vercel env pull command is invaluable for getting your local dev environment in sync with production.

3. Vercel + GitHub: import from .env (one-time)

On initial project import, Vercel can read a checked-in .env.production.exampleand auto-import the keys (you fill in values). Useful for templates; we use it in our deployment guide.

NEXT_PUBLIC_ vs server-only

Next.js inlines variables prefixed with NEXT_PUBLIC_ into the client JavaScript bundle at build time. Everything else is server-only — accessible from Route Handlers, Server Components, Server Actions, and middleware, but NOT visible to the browser.

// In a Server Component or Route Handler — both work
const apiKey = process.env.OPENAI_API_KEY       // server-only ✓
const appUrl = process.env.NEXT_PUBLIC_APP_URL  // also works ✓

// In a Client Component
const apiKey = process.env.OPENAI_API_KEY       // undefined ✗ (server-only)
const appUrl = process.env.NEXT_PUBLIC_APP_URL  // works ✓ (inlined at build)

The rule: If a variable is a secret (API key, database password, JWT signing key), it MUST NOT have the NEXT_PUBLIC_ prefix. Prefix only the non-sensitive values you genuinely need in the browser (your own app URL, analytics IDs, publishable Stripe key).

Build-time vs runtime

Vercel reads env vars at build time by default. Changing a variable doesn't affect the running deployment — you have to redeploy for the new value to take effect. This is a feature: it means the same build produces deterministic output.

Edge functions and Node serverless functions get env vars at runtimetoo. Changing a variable in the Vercel dashboard takes effect on the NEXT request to those functions (no redeploy needed). But values inlined at build (NEXT_PUBLIC_and anything used inside Server Components rendered statically) require a redeploy.

Practical implication: if you rotate an API key, redeploy. Don't rely on "just changed the env var; should work" — half your code may still see the old value.

Production, Preview, Development

Vercel has three environments per project:

  • Production — the main branch (or your designated production branch). Uses production env vars.
  • Preview — every other branch + every PR gets its own preview deployment. Uses preview env vars (typically pointing at staging databases, sandbox payment keys, etc.).
  • Development — only used by vercel dev (Vercel's local emulator). Most teams ignore this and use .env.local instead.

The most common configuration: separate DATABASE_URL, STRIPE_SECRET_KEY, etc. for Production and Preview. Production points at live infrastructure; Preview points at staging copies so QA can break things without affecting real users.

The trailing-newline gotcha

This bit us in production. If you pipe a value into vercel env add, the trailing newline gets stored as part of the value:

# BAD — adds "value\n" to the env var
echo "https://www.inbuild.io" | vercel env add NEXT_PUBLIC_APP_URL production

# Result inside your function:
process.env.NEXT_PUBLIC_APP_URL  // "https://www.inbuild.io\n"

That extra newline ends up serialized into sitemap.xml URLs, OG tag href attributes, or the second argument of fetch(). Symptoms: 404s on URLs that look correct, broken canonical tags, weird whitespace in generated HTML.

Defensive code that handles it anyway:

const APP_URL = (process.env.NEXT_PUBLIC_APP_URL || "https://inbuild.io")
  .trim()
  .replace(/\/+$/, "")  // also strip trailing slash

Or just paste values in the dashboard (no shell, no newline) — that's the simpler fix.

Rotating secrets

For an API key compromise or scheduled rotation:

  1. Generate the new value at the source (OpenAI dashboard, Stripe dashboard, etc.).
  2. Update the Vercel env var (dashboard or vercel env rm + vercel env add).
  3. Redeploy. Build-inlined values won't update until then.
  4. After confirming the new key works, revoke the old one at the source.

Don't skip the redeploy. Don't skip the revocation. Both have bit teams in production.

Related reading

Frequently asked questions

How do I add environment variables to Vercel?

Three ways: (1) Dashboard → Project → Settings → Environment Variables. (2) `vercel env add NAME` from your terminal (prompts for value + which environments). (3) `vercel env pull` to download them into a local `.env.local` file. The CLI is faster for batch setup; the dashboard is friendlier for one-offs.

What does the NEXT_PUBLIC_ prefix do?

It exposes the variable to the browser bundle. Variables WITHOUT the prefix only exist on the server (Route Handlers, Server Components, middleware). Variables WITH it get baked into the client JavaScript at build time. Never prefix secrets like API keys with NEXT_PUBLIC_ — they'll leak.

Are Vercel environment variables encrypted?

Yes. All env vars are encrypted at rest in Vercel's storage and decrypted only inside the build container or function runtime. Service-team members with appropriate permissions can read them via dashboard or CLI; nobody else (including Vercel support) can.

Can I use different env vars for preview vs production?

Yes. Each variable can be scoped to: Production, Preview (all preview branches), Development (vercel dev local runs), or any combination. Branch-specific preview env vars also work via the 'Branch' filter when adding the variable.

Why does my Vercel env variable have a trailing newline?

Adding env vars via piped stdin (`echo "value" | vercel env add NAME`) appends echo's trailing newline. The value is stored as `value\n`. When that newline gets serialized into things like sitemap.xml URLs or fetch() URLs, you get bugs. Always pass the value via `--token` or paste in the dashboard, or strip the newline in your env-reading code.

Ready to build?

Turn your next idea into a production-ready app in minutes.

Keep reading