Set Environment Variables in Next.JS
Adam C. |

Before nextJS 9.4, we can use next.config.js to set up environment variables, i.e., process.env.* which we can use in our application. 

Photo by: veeterzy

With next.config.js

Example of next.config.js like this:

module.exports = {
	// other stuffs
	env: {
		API_HOST:
		process.env.NODE_ENV !== "production"
		? "http://localhost:3030"
		: "http://localhost:5050",
		UER_LC_KEY: "DENIUSER-" + process.env.NODE_ENV,
		SITE_NAME: "DENIAPPS",
	},
};

Then, those environment variables are available in Node.js environment and browser. For example: we can use process.env.API_HOST when we fetch the data in Node.js environment, like

export const getStaticProps = async () => {
  const res = await fetch(process.env.API_HOST/posts')
  const posts: Post[] = await res.json()

  return {
    props: {
      posts,
    },
  }
}

Or use process.env.SITE_NAME in a component, like

<Menu.Item header key="menu-0">
  <Link href="/">
    <a>
       <Icon name="world" /> {process.env.SITE_NAME} 
    </a>
  </Link>
</Menu.Item>;

This works fine but has some issues: 

  1. We have to commit next.config.js to the repository, so if there are some secret environment variables, then it could be accidentally exposed.
  2. Since there is only one copy of next.config.js, if we deploy the App to a different environment, we may modify this file to have the correct variables setup, which will cause the conflict in the future. Although, we could try to avoid it by checking 'process.env.NODE_ENV', the whole setup would be messy.

With .env*

Maybe because this, in Next.js versions 9.4 and up, it comes with built-in support for .env* file.

We can now do a lot of things:

  1. use .env.*.local to save secret variables, like DB connections, etc. We have '*' in the file name, which means it includes, .env.local, .env.production.local, .env.development.local, and .env.test.local, They are all added to '.gitignore' by default.
  2. we can have .env (all environments), .env.development (development environment), and .env.production (production environment).
  3. By default all environment variables loaded through .env (.env.*.local, or .env.*) are only available in the Node.js environment, meaning they won't be exposed to the browser.
  4. In order to expose a variable to the browser we have to prefix the variable with NEXT_PUBLIC_. For example:
NEXT_PUBLIC_SITE_NAME="DENIAPPS"

Import Notes

  • When we say “use .env.*.local to save secret variables”, it only means those variable defined in the .local are not shown up in the public repository. If we defined ‘NEXT_PUBLIC_.’ variables in .env.*.local, then they are still visible in the browser.
  • .env.local is intended to override the default set, but if we have .env.development.local, then it will override the .env.local, for example: when we run 'npm run dev', it loads,
ready - started server on http://localhost:3000
info - Loaded env from /Projects/next-feathers/next/.env.development.local
info - Loaded env from /Projects/next-feathers/next/.env.local
info - Loaded env from /Projects/next-feathers/next/.env.development
info - Loaded env from /Projects/next-feathers/next/.env
event - compiled successfully

Whatever loading first will be used, so the variable defined in .env.development.local overrides the same variable defined in .env.local, which overrides .env.development, which overrides .env.

  • We cannot set PORT in .env file, and we have to pass it via start scripts, like
"start": "next start -p 8080"

or

npm run start -- --port 8082
  • Also Next.js document says,

There is a small difference between test environment, and both development and production that you need to bear in mind: .env.local won't be loaded, as you expect tests to produce the same results for everyone. This way every test execution will use same env defaults across different executions by ignoring your .env.local (which is intended to override the default set).

My Opinions

Most of environment variables we defined are probably for client side to use, so by using this new feature, all those variables have to be prefixed with NEXT_PUBLIC_., which is a bit ignoring. I would suggest to do the opposite, which is using NEXT_PRIVATE_. prefix for variables ONLY available to Node.js environment.