Overview
Next.js (since version 9.4) can use standard environment variables defined in your .env files. Prior to 9.4, the process required using the next.config.js file.
Using a naming convention, Next.js will differentiate between environment variables that are inlined
during the build process and those available at runtime.
Variables that start with NEXT_PUBLIC_
will be inlined.
# inlined, requires new build to update
NEXT_PUBLIC_ROOT_PATH=/
NEXT_PUBLIC_BASE_COLOR=lightblue
# NOT inlined, available at runtime, can be changed without a new build
API_PATH=https://api.example.com
API_KEY=aabbccxx123randomkey
What does inlining mean?
Inlining takes the value of the environment variable and hard-codes it (inlines it) into your code during the build process.
A new build is required to update those variables. Regular environment variables (not prefixed with NEXT_PUBLIC_
) can be updated without a new build.
The non-prefixed variables are what you'd expect with server side languages and frameworks like NodeJS/Express, Ruby/Ruby on Rails, and Python/Django. The prefixed variables are closer to what you get with create-react-app
With Create-React-Apps REACT_APP_ variables, and Next.js NEXT_PUBLIC_ variables, you never want to add any private/secret values here as they will become available as part of your build output that is sent to the browser.
The behavior of environment variables can be especially confusing due to the flexibility in Next.js. The following rendering models are available:
- client-side rendering (CSR)
- server-side rendering (SSR)
- static-site generation (SSG)
For this reason, both variable types are needed to accommodate the use cases.
Code Example
A simple example might help us visualize this better.
Let's create a brand new Next.js project and add a .env file and update the index.js file. The contents of each are below.
# .env file
# inlined
NEXT_PUBLIC_VALUE1=THIS IS VALUE 1
# not inlined
VALUE2=THIS IS VALUE 2
// index.js
export default function Index(){
function handleClick() {
console.log("VALUE1", process.env.NEXT_PUBLIC_VALUE1)
console.log("VALUE2", process.env.VALUE2)
}
return (
<div>
<p>VALUE1: {process.env.NEXT_PUBLIC_VALUE1} </p>
<p>VALUE2: {process.env.VALUE2}</p>
<button onClick={handleClick}>Click</button>
</div>
)
}
export async function getServerSideProps(context) {
const VALUE1 = process.env.NEXT_PUBLIC_VALUE1
const VALUE2 = process.env.VALUE2
return {
props: { VALUE1, VALUE2},
};
}
In this example, we have two variables defined in the .env file.
- NEXT_PUBLIC_VALUE1 - will be inlined
- VALUE2 - will not be inlined
Our index file uses these variables in multiple ways (some not recommended).
- In the
getServerSideProps
method - called each time this page is requested. - In the JSX - called during server-side rendering, and again to hydrate the client side.
- In the handleClick event handler - only triggered from the client.
Note: exact behavior during development and final build can vary, so let's build our project and test. We can build our project by running yarn build && yarn start
Once the project finishes building, you'll be able to inspect the build output in the .next directory.
This directory has two top level directories. next/server
and next/static
. In general, this is how these directories are used.
The files in next/server
are returned on a hard refresh, if JavaScript is disabled, or if it's the first page the user requests for this site.
The files in next/static
are returned when the user is navigating from page to page on the site.
As you can see from the image above. The locations where NEXT_PUBLIC_VALUE1
were defined have been replaced with their literal value during the build.
However, the VALUE2
continues to reference process.env
.
The video below shows the effects of trying to use a non NEXT_PUBLIC
env var in your JSX/markup code. The value is available on server response, but not on client hydration.
How to decide?
Choosing whether to use PUBLIC or NON-PUBLIC environment variables is difficult when starting out.
Here are some questions you should ask yourself.
- Is it needed on the client, server, build-time or a combination?
- does it change by environment (i.e. dev, staging, production)?
- Is it private or public value (i.e. API Location, Secret Key)?
- Where/when is the build happening (once, in each environment)?
To simplify, here are some rules you can follow:
- Use
NEXT_PUBLIC_
variables for JSX/Markup. - Don't use
NEXT_PUBLIC_
variables in your server methods (i.e. getStatProps, getStaticPaths, getServerSideProps) - NEVER put secret/private values in your
NEXT_PUBLIC_
variables.
This Article is in Draft / Incomplete Status
This article is currently being written. Expect frequent updates and completion within the next few weeks. I often release early to motivate myself to finish the articles :)