Blog

The correct way to load environment variables in Next.js

Load environment variables with build-time type-safety in Next.js 15

Published June 2, 2025 (Updated June 2, 2025)

This article has been updated for the latest Next.js version 15

GitHub repository: https://github.com/AustinShelby/the-correct-way-to-load-environment-variables-in-nextjs

This will be the only post you will ever need on this subject, and I can guarantee you will never again crash your production environment because you mistyped API_KEY.

If you have ever written code that looks like this:

const url = `https://www.example.com/?api_key=${process.env.API_KEY}`
TypeScript bad environment variable example

Then you might have a nasty bug on your hands!

Here's what I mean.

In a scenario where you build the application without having set the API_KEY environment variable the application will use undefined instead.

Obviously undefined is not the correct api key which will make any request using that URL fail.

The problem here is that when the error surfaces, the message will be very misleading and look something like this:

Error: Unauthorized

And this error will only show up when you send a request to the URL.

If calling the API is an essential feature, the application should not have even been built without the API key being available.

Naively expecting the API_KEY environment variable to exist will hide the bug and make this problem a pain to debug due to the misleading error message.

When a problem exists that causes the application to not function, the application needs to fail immediately and visibly.

How to load environment variables in Next.js with type-safety

Create a .env file. Here you will put all of your environment variables you want to use on your local development environment.

.env
API_KEY=secret
NEXT_PUBLIC_MESSAGE=hey

Next.js automatically adds this file to .gitignore so you don't have to worry about it ending up in your version control system.

All environment variables are available on the server-side.

Only environment variables that are prefixed with NEXT_PUBLIC_ are available on the client-side.

Now to the bread and butter.

Validating environment variables for type-safety

Create a validateEnvironmentVariables.ts file with this code to validate the environment variables and extend the NodeJS namespace in a type-safe way.

validateEnvironmentVariables.ts
1import { z } from "zod";
2import env from "@next/env";
3
4const environmentVariables = z.object({
5 API_KEY: z.string(),
6 NEXT_PUBLIC_MESSAGE: z.string(),
7});
8
9type EnvironmentVariables = z.infer<typeof environmentVariables>;
10
11const projectDir = process.cwd();
12
13env.loadEnvConfig(projectDir);
14
15environmentVariables.parse(process.env);
16
17declare global {
18 namespace NodeJS {
19 interface ProcessEnv extends EnvironmentVariables {}
20 }
21}
Validate environment variables in Next.js

Let's break it down line by line and see what it does.

On lines 1 and 2, we import zod, which we use to validate our environment variables and @next/env which we use to load the environment variables from the local .env file.

On lines 4 to 7, we define the schema for our environment variables. We define a required string schema for the variables API_KEY and NEXT_PUBLIC_MESSAGE that we defined in our .env file.

On line 9, we infer a type from our zod schema.

On line 11, we save our current working directory location to a variable projectDir.

On line 13, we use the loadEnvConfig to load environment variables either from the local .env file.

On lines 15 to 19, we extend the ProcessEnv interface with the type of our zod schema.

To summarize, we load the environment variables either from a .env file or from the environment and validate that they fit our schema. Then we extend the NodeJS namespace with the type of our schema so we get type-safe autocompletions in our IDE.

Adding a build time check

To run this environment variable check before we build our application, we can add a pre-build step in our package.json file.

package.json
1{
2 "name": "the-correct-way-to-load-environment-variables-in-nextjs",
3 "version": "0.1.0",
4 "license": "MIT",
5 "scripts": {
6 "dev": "next dev",
7 "build": "next build",
8 "start": "next start",
9 "lint": "next lint",
10 "predev": "node --experimental-strip-types validateEnvironmentVariables.ts",
11 "prebuild": "node --experimental-strip-types validateEnvironmentVariables.ts"
12 }
13}
14
Pre-build environment variable validation

On line 10, we add the predev command which runs before you run npm run dev automatically.

On line 11, we add the prebuild command which runs before you run npm run build automatically.

Note: You need to use Node.js version 22.6.0 or higher. If you are using Node.js version 23.6.0 or higher, you can remove the --experimental-strip-types.

Extra

Create a .env.example file so anyone who pulls your repository knows what environment variables they should set.

.env.example
API_KEY=
NEXT_PUBLIC_MESSAGE=

Also add the following line to your .gitignore file to make sure the .env.example file gets committed to the repository.

.gitignore
!.env.example

Why this is the correct way to load environment variables

In a case where you forgot to add the environment variables API_KEY or NEXT_PUBLIC_MESSAGE, the application won't even build, and it will throw an error saying that the environment variable wasn't found.

Our application now fails immediately and visibly.

This is called failing fast.

It is part of the clean code principles, which you can read more about here: https://www.martinfowler.com/ieeeSoftware/failFast.pdf.

Because we are using TypeScript, we can be 100% sure that all the environment variables exist.

Additionally, TypeScript helps us with autocompletions.

Conclusion

While this might seem pedantic, keeping these small things in your mind while writing code will make you a better software engineer.


Austin Shelby

Austin Shelby

I am a freelance software engineer, instructor, and public speaker with over half a decade of hands-on experience in the entire software development process. I am passionate about building high-performing web applications and sharing what I have learned along the way.