How to use environment variables in Next.js

2024/05/09

(This guide uses the app router version of Next.js but some of it may still be applicable to the page router version)

Using environment variables in Next.js can be very confusing. Searching around the web it doesn't feel like anyone is solving the issue of "how do I get env variables into a Next.js container?". That is why I created this guide, to explain how this can be done. This guide is also complimented by an example repo which can be found here.

Understanding environment variables in the Next.js context

Environment variables can be supplied to Next.js in multiple ways. How they are supplied and how your Next.js application is set up has a big impact on what is possible.

.env files

When reading through the documentation it seems like using the .env files are the way to go. These files, depending on their name make variables accessible through the field process.env.{variable name}. The .env files you would usually create are:

  • .env.local - The contents in this file is accessible no matter what your Next.js environment is.
  • .env.development - The contents in this file are only accessible when running in dev mode npm run dev.
  • .env.production - The contents in this file are only accessible when using a production image.

Provided environment variables

Environment variables that are present on your system or in the container of the Next.js app are also available. These can also be accessed through the process.env.{variable name} field. However, since most Next.js pages are statically generated, these values won't change even if you give the container new and different variables. There is a way to circumvent this which I will explain in the next section.

Environment variables in practice

"If both .env files and provided environment variables don't work because of being statically generated, how am I supposed to provide new values?"

Great question. There are multiple ways to use provided environment variables instead of the baked in ones.

Using unstable_noStore() to trigger dynamic rendering

unstable_noStore() which is described here, makes it so the component is rendered server-side when requested and will therefore use the current environment variables. Simply call it in your component like so and it should work:


_10
import { unstable_noStore } from "next/dist/server/web/spec-extension/unstable-no-store";
_10
_10
function Component() {
_10
unstable_noStore();
_10
_10
return(
_10
<p>{process.env.providedValue}</p>
_10
)
_10
}

This method is prepended with unstable_ because there is a high chance it may change in a future release of Next.js.

Using a server action to retrieve environment variables

A server action is run on the server when requested. Meaning it will use the environment variables currently defined. Define an action in the src/actions folder like this:


_15
'use server'
_15
_15
interface EnvVariableResult {
_15
theVariable: string | undefined
_15
}
_15
_15
async function EnvVariable(): Promise<EnvVariableResult> {
_15
'use server';
_15
_15
return {
_15
theVariable: process.env.actionVariable || undefined
_15
}
_15
}
_15
_15
export default EnvVariable;

This defines a simple server action that can be called from another component. The function must be async to be a server action. This variable can then be used in a client component like this:


_22
'use client';
_22
_22
import React, {useEffect, useState} from 'react';
_22
import EnvVariable from "@/actions/test";
_22
_22
function ClientComponent() {
_22
const [theVariable, setTheVariable] = useState<string | undefined>(undefined);
_22
_22
useEffect(() => {
_22
EnvVariable().then(result => {
_22
setTheVariable(result.theVariable);
_22
});
_22
}, [])
_22
_22
if (theVariable) {
_22
return (
_22
<p>{theVariable}</p>
_22
)
_22
}
_22
}
_22
_22
export default ClientComponent;

This should render the environment variable. Calling this from a normal server-side component that isn't using unstable_noStore() will not result in anything, as that component was rendered at compile-time.

The example doesn't return anything til theVariable is set because it won't be ready when initially rendered.

What is the best practice?

To avoid using useEffect I would definitely go with using unstable_noStore() server side. This is the simplest approach and makes it so you won't have to use 'use client' when not necessary. To still use environment variables in a client component I would recommend passing them in from a parent server component, this way you avoid the async complexity of having to use the server side action.

Example project

I have created an example project which can be found here. It shows how rendering with unstable_noStore() and without impacts environment variables. The readme goes into detail with this, so you don't have to run the project yourself.

Conclusion

Congrats! You should now know how to handle environment variables in Next.js. I hope this was able to help you 😊.