redhot coding logo
freelancer
..building the future..
← Back

Making sense of Next JS environment variables

Working with environment variables is a bit like keeping track of your house keys: super easy until you misplace them at the wrong time—and then, bam! You're locked out.

Trust me, I've spent a fair amount of time debugging what turned out to be misconfigured environment variables. So, I thought I'd save you some headaches by sharing everything I know about getting them right in Next JS.

In this post, I'll walk you through how to set up and use Next JS environment variables and share some of my best practices for keeping things organized.

Why Environment Variables Matter in Next.js

First things first: environment variables are those little configuration helpers that let you manage data outside of your code.

If you're like me, you don't want API keys, database URLs, or secret tokens floating around in your codebase. Not only does it keep things cleaner, but it's safer. If you're working with other developers, sharing code without exposing sensitive details becomes far simpler.

And with environment variables, you're not only protecting these details but also making sure your application can handle different settings for development, testing, and production. It's the secret sauce for flexibility.

In Next.js, environment variables are easy to work with—but they come with some quirks you'll want to watch out for.

Setting Up Your Environment Variables in Next JS

When using technologies such a Node, Express, or Gatsby, we need to use a library like dotenv to load environment variables from .env files. But with Next JS, things are a little more streamlined. Next JS automatically loads environment variables from your .env files, so you don't need any extra configuration. Sweet, right?

When you deploy your Next JS app to a hosting service like Vercel or Netlify, you must set the environment variables in the hosting service's web dashboard. Any .env files in the git repo will be ignored.
In that case, you'll still need to include them in your project because they will be used on your local machine when you run the code during development.

Specifying environment files in an .env file:

This is how you specify the environment files in an .env file:

# .env
URL=https://example.com
Environment files must be put in the root of your project folder.
You can use different kinds of .env file, as you'll see in the next section.

Accessing Environment Variables in your Code:

Accessing environment variables in Next JS is easy. Just use process.env:

const url = process.env.URL;

Accessing Environment Variables Outside Next JS Runtime

Sometimes, you might need to access environment variables outside of Next JS's runtime, like in a standalone script or configuration file. For this, Next JS provides a helpful helper called loadEnvConfig from @next/env, which lets you load your environment variables anywhere.

Here's how to use it:

import { loadEnvConfig } from '@next/env';
const projectDir = process.cwd();
loadEnvConfig(projectDir);
This is a lifesaver when working with configuration files or scripts that aren't directly part of the Next JS application. With loadEnvConfig, you can make your .env values accessible even in separate parts of your project.

Different types of .env file in Next JS

Next JS supports several .env files to manage environment-specific variables.

Here's a rundown of the various Next js environment variable files and how they work:

  • Default .env file:

    .env:

    This is the base configuration file. Variables in this file are valid in all environments and on all machines. If the same values are also present in other, more specific .env files, they will be overridden by those values.

    Example:

    # .env
    # Default values, for all environments and machines
    # Don't add this file to .gitignore!
    
    # backend variables
    LOG_LEVEL=info
    
    # variables for backend AND frontend
    NEXT_PUBLIC_APP_NAME=MyApp
    NEXT_PUBLIC_API_VERSION="v1"
    NEXT_PUBLIC_API_TIMEOUT=10000
    					
    Anything you want the frontend to access needs to start with NEXT_PUBLIC_. This is a big deal: without it, your variable won't be available to the frontend. Trust me, I've wasted hours trying to figure out why a variable "didn't exist," only to realize I forgot the NEXT_PUBLIC_ prefix.

    But: Avoid Overuse of NEXT_PUBLIC_: Limit the number of variables prefixed with NEXT_PUBLIC_ to only what's necessary. Exposing too much info to the client can lead to security issues and unnecessary data exposure.

    Note: NODE_ENV is automatically available in the browser without having to prefix it with NEXT_PUBLIC_.

  • Environment-specific .env files:

    Each of these files is read based on the environment you're in, which is decided by the NODE_ENV setting. This setting is automatically filled in by Next JS when you run certain commands.

    By default, next dev sets NODE_ENV to "development", next build sets it to "production", and test frameworks like Jest set it to "test".

    .env.development:

    This file holds your development environment configuration. It's where you store values that:

    • are strictly for development (and will be different from production)
      (like using a local database instead of the production one)
    • are consistent across all machines running the app.
      (so, no machine-specific settings here!)
    • are safe to commit to your git repo
      (keep those API keys and passwords out of here!)
    Example:
    # .env.development
    # Values for development mode only
    # Don't add this file to .gitignore!
    
    # backend variables
    LOG_LEVEL=debug
    SMTP_HOST=smtp.mailtrap.io
    DATABASE_STRING="postgres://$DB_USER:$DB_PASSWORD@localhost:$DB_PORT/myapp_db_dev"
    
    # variables for backend AND frontend
    NEXT_PUBLIC_API_URL="http://localhost:3000/api"
    					
    Notice that we've put the LOG_LEVEL variable in both .env and .env.development.
    When running in development mode, Next JS will use the value from .env.development, because .env.development is more specific than .env.
    Even though the value of NEXT_PUBLIC_API_URL could be different on the development machines of different team members (ie different port), we've put it in .env.development because this is what will normally be used in development.
    If some developer needs to override this value for their local machine, they can do so by adding a .env.development.local file to their project folder (see further).
    For the DATABASE_STRING variable, we're using a neat trick here:
    you can reference one environment variable from another within your .env files. As long as you put the variable name in quotes, and include the variable name prefixed with a dollar sign, Next js will replace the variable name with the actual value.

    We'll put the referenced variables in a separate file, because they are not safe to be shared in the git repo, or are machine-specific (see further down for more on this).

    .env.production:

    This file is crucial for your production setup. It's where you define the environment variables that:

    • are exclusive to production (and differ from development)
      (like connecting to your live database instead of the local one)
    • are universal, no matter what machine the app is deployed on.
      (so, avoid any machine-specific settings!)
    • are safe to share in your git repo
      (again, no sensitive info like API keys or passwords!)
    Example:
    # .env.production
    # Values for production mode only
    # Don't add this file to .gitignore!
    
    # backend variables
    SMTP_HOST=smtp.sendgrid.net
    DATABASE_STRING="postgres://$DB_USER:$DB_PASSWORD@localhost:$DB_PORT/myapp_db_prod"
    
    # variables for backend AND frontend
    NEXT_PUBLIC_API_URL="https://api.myapp.com/api"
    					

    .env.test:

    This file is your testing environment's best friend. It's where you define variables that:

    • are specific to your testing setup (different from both development and production)
    • are the same no matter what machine it is run on
    • are safe to include in your version control
    This setup ensures that your tests are isolated and don't interfere with your development or production environments.

  • Machine-specific .env file:

    Machine-specific (local) .env files are used to store:

    • values which only work on a local machine
    • sensitive values
    • values which are not safe to be shared in the git repo
    • personal values which are developer-specific (personal username, password, etc)
    • temporary values for testing by a developer on a local machine
    They should NOT be shared in the git repo, and must be included in .gitignore.

    .env.local:

    This file overrides environment variables in .env.

    For example:
    # .env.local
    # Machine-specific default values
    # This file MUST be added to .gitignore!
    
    # backend variables
    
    
    # variables for backend AND frontend
    NEXT_PUBLIC_API_TIMEOUT=2000
    					
    Note that the developer has added NEXT_PUBLIC_API_TIMEOUT to .env.local, overriding the value in .env, to get a shorter timeout for local development.

  • Environment-specific AND machine-specific .env files:

    .env.development.local:

    The values in this file will override the values in .env.development and .env.local. This is useful for setting up local development variables, when you want to set values specific to your local machine, or simply want to test with different values than the shared development variables.

    Example:

    # .env.development.local
    # Local values for local development in development mode (next dev)
    # This file MUST be added to .gitignore!
    
    # backend variables
    DB_USER=mylocaldevuser
    DB_PASSWORD=mylocaldevpassword
    DB_PORT=5432
    
    # variables for backend AND frontend
    NEXT_PUBLIC_API_URL="http://localhost:3001/api"
    					
    Note that the developer has added NEXT_PUBLIC_API_URL to .env.development.local, overriding the value in .env.development, to account for the different port number used by the local development server.

    .env.production.local:

    The values in this file will override the values in .env.production and .env.local.

    This file is useful for 2 situations:

    1. local development:
    When a developer wants to test the production environment on their local machine, they can add values to this file to override the production values.

    2. self-hosted production server:
    When the production server is not hosted by a specialized hosting service such as Vercel or Netlify (where the environment variables are set from the hosting provider's web dashboard instead of from .env files), the developer can add server-specific values in this file.
    On hosting services like Vercel or Netlify, none of the .env files included in the repo will be used!

    Example (on developer's local machine):
    # .env.production.local
    # Local values for local development in production mode (next build)
    # This file MUST be added to .gitignore!
    
    # backend variables
    DB_USER=mylocalproduser
    DB_PASSWORD=mylocalprodpassword
    DB_PORT=5432
    
    # variables for backend AND frontend
    					

    Example (on self-hosted production server):
    # .env.production.local
    # Values for live production server
    
    # backend variables
    DB_USER=productionuser
    DB_PASSWORD=productionpassword
    DB_PORT=5432
    
    # variables for backend AND frontend
    					
    Note: you will have to manually copy or create this file on the self-hosted production server, because it is not included in the git repo.

Which Environment File Gets Priority in Next JS?

Next JS loads environment variables in a specific order based on your environment. It takes the first value it finds and ignores duplicates from lower-priority files. So to know which file will be used, you need to know the priority order. Files on the left have higher priority than files on the right.

Here's how it works:

  • When NODE_ENV=development:

    .env.development.local.env.local.env.development.env

  • When NODE_ENV=production:

    .env.production.local.env.local.env.production.env

  • When NODE_ENV=test:

    .env.test.local.env.test.env

    Note that .env.local is not used when running tests!

Next JS .env Variables Are Injected at Build Time

Here's a common Next JS gotcha: when you update any environment variable in your .env files, you'll need to restart your dev server to see those changes take effect. Next JS loads environment variables when the server starts, so any edits after that don't automatically apply.

This becomes even more important for production or static builds, where environment variables get “locked in” at build time. If you change any variables and want them to be reflected in your build, you'll need to rebuild the app. I've definitely spent time scratching my head over this, only to realize that my application just needed a quick restart or rebuild to pick up the changes!

Common Pitfalls with Environment Variables in Next JS

Now that we understand the basics of Next.js environment variables, let me share some additional challenges I've encountered in real-world projects. These are the kind of things you might not find in the official docs:

  • Variable Changes Need a Restart:

    As mentioned above: after updating, adding or removing environment variables, make sure to restart the Next.js dev server or rebuild your project or the changes won't have effect.

  • Avoid Sensitive Data in your Git Repository:

    One of the easiest ways to accidentally expose secrets is by committing them to your Git repository.
    Only ever include sensitive data in local environment files (ie .env.local)!
    Always add local environment files to .gitignore to keep them out of your repo!
    And if you accidentally push sensitive info, be sure to change the values immediately and remove them from the repo history.

  • Document Your Variables:

    Since you're not committing your local environment files to the git repo, anyone else pulling the repo won't have an example of how to set them up. Consider creating a .env.local.template or .env.local.example file with placeholders for each variable, as well as environment-specific versions. Not only does this keep everyone on the same page, but it's also a great reference for what each variable does, especially in team projects.

  • Prefix Client-Side Variables Properly:

    Only variables prefixed with NEXT_PUBLIC_ are available in the browser. Missing this prefix when you need access in the frontend will lead to undefined errors, while overusing it can expose sensitive info.

  • Don't Try to Destructure process.env:

    If you ever try to destructure process.env, you'll run into issues:

    const { NEXT_PUBLIC_URL } = process.env;
    It won't give you an error, but NEXT_PUBLIC_URL will always be undefined! This is because the environment variables in process.env are fetched dynamically when accessed.

Final Thoughts

Environment variables are crucial for keeping your Next JS application clean and secure, but they also have a few quirks that can be confusing at first. Next JS gives you a lot of power and control with these variables—just be sure you're taking full advantage of it without putting sensitive data at risk.

Hopefully, this guide has helped make sense of some of the trickier points.

Frequently Asked Questions

If process.env is undefined in Next.js, it's usually due to one of these reasons:

  • You're trying to access the variable on the client side without the NEXT_PUBLIC_ prefix
  • The environment variable was added after starting the development server (requires restart)
  • You're destructuring process.env (which doesn't work in Next.js)

To fix this:

  1. Add NEXT_PUBLIC_ prefix if you need the variable in client-side code
  2. Access variables directly: process.env.MY_VARIABLE instead of destructuring
  3. Restart your development server after adding new environment variables

Common reasons why Next.js environment variables might not be working:

  • Environment file is in the wrong location (should be in root directory)
  • Incorrect file or variable naming (check for typos in .env files)
  • Missing NEXT_PUBLIC_ prefix for client-side usage
  • Server hasn't been restarted after adding variables
  • The value is taken from the wrong environment file (check file precedence)

Quick troubleshooting steps:

  1. Verify your environment file is in the project root
  2. Double-check variable and file names and prefixes
  3. Check if the value is defined in the correct environment file (check file precedence)
  4. Restart the development server

About the Author

Ruben Lemiengre

Ruben Lemiengre is a full-stack Javascript application developer with 10+ years of experience, passionate about web development for the future and creating web applications with the potential to disrupt industries.