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
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, so you don't need any extra
configuration. Sweet, right?
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
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 withNEXT_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 theNEXT_PUBLIC_
prefix.
But: Avoid Overuse ofNEXT_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 withNEXT_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
setsNODE_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!)
# .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 theLOG_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 ofNEXT_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!)
# .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
- are strictly for development (and will be different from production)
-
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
.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 addedNEXT_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 addedNEXT_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:
It won't give you an error, butconst { NEXT_PUBLIC_URL } = process.env;
NEXT_PUBLIC_URL
will always be undefined! This is because the environment variables inprocess.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:
- Add
NEXT_PUBLIC_
prefix if you need the variable in client-side code - Access variables directly:
process.env.MY_VARIABLE
instead of destructuring - 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:
- Verify your environment file is in the project root
- Double-check variable and file names and prefixes
- Check if the value is defined in the correct environment file (check file precedence)
- Restart the development server