Vercel environment variables don’t just store values; they actively shape your application’s behavior across different deployment stages, often in ways that surprise people.

Let’s see this in action. Imagine a simple Next.js app with a NEXT_PUBLIC_API_URL that points to a different backend depending on the environment.

// pages/index.js
function HomePage() {
  const apiUrl = process.env.NEXT_PUBLIC_API_URL;
  return (
    <div>
      <h1>Welcome!</h1>
      <p>API Endpoint: {apiUrl}</p>
    </div>
  );
}

export default HomePage;

In your Vercel project settings, you’d configure these variables.

  • Development: These are typically managed locally via a .env.local file. Vercel’s CLI respects this file during vercel dev.

    # .env.local
    NEXT_PUBLIC_API_URL=http://localhost:3001/api
    

    When you run vercel dev, your app will see http://localhost:3001/api.

  • Preview: These variables are set for specific Git branches or pull requests. When you push to a branch like feature/new-auth, Vercel automatically creates a preview deployment and injects the preview environment variables. You’d set these in the Vercel dashboard under your project settings -> Environment Variables.

    • Key: NEXT_PUBLIC_API_URL
    • Value: https://staging.myapp.com/api
    • Environment: Preview
    • Scopes: Select the feature/new-auth branch. Now, anyone previewing that branch will see https://staging.myapp.com/api.
  • Production: These are for your main deployed application, typically on the main or master branch.

    • Key: NEXT_PUBLIC_API_URL
    • Value: https://api.myapp.com/api
    • Environment: Production
    • Scopes: Select the main branch. Your live site will now use https://api.myapp.com/api.

The core problem Vercel environment variables solve is managing configuration drift between different deployment environments without resorting to cumbersome build-time configurations or hardcoded values. They allow a single codebase to behave differently based on where it’s deployed. The NEXT_PUBLIC_ prefix is crucial for client-side accessibility; variables without it are only available on the server.

Internally, Vercel injects these variables into the execution environment of your serverless functions and your frontend build. For serverless functions, they are available via process.env. For the frontend, Vercel’s build process substitutes process.env.NEXT_PUBLIC_... with their actual values during the build. This means NEXT_PUBLIC_ variables are bundled into your client-side JavaScript.

The exact levers you control are the Key, the Value, the Environment (Development, Preview, Production), and importantly, the Scopes. Scopes allow you to granularly control which Git branches or pull requests inherit specific environment variables. For instance, you might have a DATABASE_URL that’s only accessible by your production deployment and not by any preview deployments, enhancing security. You can also assign variables to all environments, but this is less common for sensitive data.

A common point of confusion is how Vercel prioritizes environment variables when multiple scopes might apply. For instance, if you have a variable set for "Production" and also for a specific branch like main (which is also Production), Vercel prioritizes the more specific scope. If a variable is defined for a specific branch (feature/xyz) and also for "Preview," the branch-specific one takes precedence for deployments originating from that branch. If a variable is defined for "Preview" generally and also for a production deployment associated with a branch that matches a "Production" scope, the "Production" scope will win.

The next concept you’ll likely grapple with is how to handle secrets that should never be exposed client-side, even with NEXT_PUBLIC_.

Want structured learning?

Take the full Vercel course →