Vercel Hobby · Beachhead course

Lesson 3 of 6 · about 20 minutes

How environment variables work

The win One variable, three values: your app shows a different name in Production, in Preview and on your laptop, and you can say why a changed value did not show up until you redeployed.

This lesson uses a harmless value on purpose. Get the mechanics right with something that cannot hurt you, and the real keys in Lessons 4 and 5 become routine.

First, recall

After the first deployment, what kind of deployment does pushing a non-production branch create?

A Preview deployment, at its own generated URL. (Lesson 2)

What did vercel link create, and what is in it?

A .vercel directory with the project and org configuration. It contains no secrets and is ignored by Git.

Three facts carry the whole topic

1. A variable is a name, a value, and a list of environments. The same name can hold a different value in Production, in Preview and in Development. That is how one codebase talks to a live database in one place and a scratch database in another.

2. A deployment keeps the values it was created with. Changing a value in the dashboard changes nothing that is already running. The new value arrives with the next deployment, so after a change you redeploy.

3. In Next.js, the name decides who can see the value. A name beginning NEXT_PUBLIC_ is copied into the JavaScript sent to every visitor, at build time. Every other name stays on the server.

Tested, and worth knowing “Stays on the server” protects the JavaScript bundle. It does not protect you from yourself. A test page built for this course printed a server-only variable in its JSX: the value was absent from every file under .next/static, and it was sitting in the page's HTML, because the server rendered it there. The rule that follows: read secrets only inside Route Handlers and other server code, and never put one in anything you return.

Where the values live on your laptop

Next.js reads a file called .env.local when you run npm run dev. You do not write that file by hand. vercel env pull downloads the project's Development values into it. create-next-app already ignores .env* in Git, so the file stays off GitHub.

Do it

Run these from the beachhead folder, on main.

  1. Add one name with three values. The value is public by design, so passing it on the command line is fine here. --no-sensitive keeps it readable later.

    vercel env add NEXT_PUBLIC_APP_NAME production --value "Beachhead" --no-sensitive --yes
    vercel env add NEXT_PUBLIC_APP_NAME preview --value "Beachhead (preview)" --no-sensitive --yes
    vercel env add NEXT_PUBLIC_APP_NAME development --value "Beachhead (dev)" --yes
  2. Look at what you made.

    vercel env ls

    Three rows, one per environment. The same list is in the dashboard under the project's Environment Variables page.

  3. Pull the Development value down.

    vercel env pull

    Open .env.local and find NEXT_PUBLIC_APP_NAME. Then run git status: the file is not listed, because it is ignored.

  4. Use it. Replace the home page with one line that shows the name.

    app/page.tsx

    export default function Home() {
      return <h1>{process.env.NEXT_PUBLIC_APP_NAME ?? "unnamed"}</h1>;
    }

    Run npm run dev and open localhost:3000. It says Beachhead (dev).

  5. Ship it.

    git add -A
    git commit -m "Show app name from environment"
    git push

    When the deployment is ready, your production domain says Beachhead. Same code, different value.

  6. Predict, then observe. Change the Production value:

    vercel env update NEXT_PUBLIC_APP_NAME production --value "Beachhead v2" --yes

    Before you reload the production page, say out loud what it will show.

    Reload it. What happened, and why?

    It still says Beachhead. The running deployment was built with the old value, and a NEXT_PUBLIC_ value is baked into the JavaScript at build time.

  7. Redeploy. In the dashboard open Deployments, find the latest production deployment, open its ⋯ menu and choose Redeploy. When it finishes, reload: Beachhead v2.

That last pair of steps is the most common Vercel confusion there is. You have now seen it happen under controlled conditions.

Check yourself

You change a variable's value in the dashboard. When does your live production site start using it?

  • Immediately, on the next request
  • After the next production deployment
  • Within minutes, after cache expiry

Changes “only apply to new deployments.” Nothing running is touched until you deploy again. Source

In a Next.js app, which variable name would place its value inside JavaScript sent to browsers?

  • NEXT_PUBLIC_API_KEY
  • PUBLIC_API_KEY
  • API_KEY_PUBLIC

Only the exact prefix NEXT_PUBLIC_ triggers inlining. That also makes this name a mistake: an API key must never carry the prefix. Source

You run vercel env pull with no arguments. Which values does it write, and where?

  • Production values into .env.local
  • Development values into .env.production
  • Development values into .env.local

The CLI's own help says the default file is .env.local and the default environment is development. Production values stay on Vercel unless you ask for them with --environment.

A component reads a server-only secret and renders it in its JSX. What happens?

  • The build fails with error
  • It ships in the HTML
  • It renders as empty text

Nothing stops you. The server has the value, renders it, and sends the HTML to the visitor. The missing prefix only keeps the value out of the browser's JavaScript bundle. This was tested on Next.js 16.3.8 for this course.

Read this Environment variables in the Vercel docs, then the short “Bundling Environment Variables for the Browser” section of the Next.js guide. The recipes are collected on the env var playbook.

Ask your teacher If a prediction in step 6 surprised you, ask about it in your Claude Code session before moving on. Lesson 4 depends on it.