86

I would like to have different configuration files for the environment variables and be able to use them in my next project. I saw the example with dotenv.

But I don't like to define the variables in the .env file and also define them in the config.next.js file. if for some reason I put the variables in the .env file but forget to put them in the config.next.js file the code starts having problems. Theres is a way to do it more eficiently?

My scripts in package.json:

"scripts": {
    "dev": "next",
    "build": "next build",
    "start": "next start",
    "lint": "eslint pages --ext .ts,.tsx,.js",
    "test": "jest",
    "commit": "git-cz",
    "dev:production": "dotenv next"
},

My .env vars

TITULO=react, typescript, material ui App

Component

import { NextPage }          from 'next';
import { FunctionComponent } from 'react';

interface HelloWorldProps {
  nombre: string,
  saludo?: string
}


const HelloWorld: FunctionComponent<HelloWorldProps> = ({ nombre, saludo = 'noches' }: HelloWorldProps) => (
  <>
    <h1>Hola {nombre} buenas {saludo}</h1>
    {/* eslint-disable-next-line multiline-ternary */}
    <h2>{process.env.TITULO ? 'hola' : 'adios'}</h2>
  </>
);

const Home: NextPage = () => <HelloWorld nombre="cristian" />;

export default Home;

1

14 Answers 14

101

Next 9.4 has built-in support for .env files: https://nextjs.org/docs/basic-features/environment-variables

But, in case you want to have multiple .env files, like:

  • .env.development
  • .env.staging
  • .env.prestaging
  • .env.production

It would be impossible to do with a built-in env variables support. There's only 3 environments that officially supported for now, it's: "development", "test", "production". With next dev you use "development", next build && next start uses "production" environment.

If you need to build for production environment, but using ".env.staging" for example, then you need to add env-cmd package, and add this line to your package.json:

"build:staging": "env-cmd -f .env.staging yarn build && yarn start"

Next would make a production build with ".env.staging" variables.

Sign up to request clarification or add additional context in comments.

10 Comments

With next dev you use "development", next build && next start uses "production" environment and how to run in "test" environment then?
This is ok, but it wont work with the Docker build once run anywhere as you would need to build with different commands. This is the solution: github.com/andrewmclagan/react-env
Great tip about cmd-env. Btw, looks like it should be an option on nextJs =)
@WendellPereira It definitely does, it is ridiculous we need to install external libraries for handling environments --'
This does not work with next: 9.5.3 and env-cmd: ^10.1.0. It keeps load the production env no matter what env supplied to env-cmd: $ env-cmd -f .env.development next start info - Loaded env from /Users/username/projects/ex/.env.production
|
41

The issue with most of these answers is they go against the principle of "build once run everywhere", in reality most of us are using this technique build and run with Docker containers. It's not possible to have multiple build commands like this, and it would be bad practice.

Better have your environment available at runtime. We created a package that allows next static optimisation and still have runtime env vars via window.__ENV

https://github.com/andrewmclagan/react-env

This works by generating an environment config object at runtime from whitelisted env vars:

{
  ...
  "scripts": {
    "dev": "react-env -- next dev", // where .env.${APP_ENV}
    "start": "react-env --env APP_ENV -- next start" // where .env.${APP_ENV}
  }
  ...
}

7 Comments

Coming from other environments and being absolutely dumbfounded that NextJS does it this way, thank you for being a voice of reason.
Also been googling around for a while, just confused at the "default" way env is handled in next - thank you
How to pick an API base URL from the process environment after the build? If the actual URL is unknown during the build and will be passed to the Docker container on startup?
see the documentation for this package. You can do that.
I was supremely frustrated to find that NextJS expected me to build seperate bundles for staging and production environments, this package is a great approach to solving a common problem and should be part of the core framework
|
23

You can have different .env files in nextjs with following two ways:

1. Using env-cmd package

Provide the path to your environment file in the scripts like:

"scripts": {
    "start": "env-cmd path/to/prod/env/file next start",
    "start:dev": "env-cmd path/to/prod/env/file next dev",   
    "build:dev": "env-cmd path/to/dev/env/file next build",
    "build:test": "env-cmd path/to/test/env/file next build",
    "build:stage": "env-cmd path/to/stage/env/file next build",
    "build": "env-cmd path/to/stage/prod/file next build",        
},

2. Using dotenv package

In your next.config.js file add following:

require("dotenv").config({ path: `${process.env.ENVIRONMENT}` });

module.exports = {
      // your configs
}

and in your scripts, provide that ENVIRONMENT variable like:

"scripts": {
    "start": "ENVIRONMENT=path/to/prod/env/file next start",
    "start:dev": "ENVIRONMENT=path/to/dev/env/file next dev",
    "build:dev": "ENVIRONMENT=path/to/dev/env/file next build",
    "build:test": "ENVIRONMENT=path/to/test/env/file next build",
    "build:stage": "ENVIRONMENT=path/to/stage/env/file next build",
    "build": "ENVIRONMENT=path/to/stage/prod/file next build",        
},

NOTE: The thing is do not to put your .env* files in the root folder, otherwise NEXT will auto-pick from your .env* files and it only supports production and development stages. So it'll ignore other .env.my-stage files.

4 Comments

hi. is the env-cmd needed for the next start command? I think the variables are inlined during the build and next start just runs that build, am I right?
@RichardTrembecký, yes that's correct. Without production build, next start won't work and env-cmd injects variables during build.
Module not found: Error: Can't resolve 'fs' in 'C:\admin\node_modules\dotenv\lib'
for env-cmd, you can use -f to force using a specific env file if your envs are on root folder: "env-cmd -f .env.dev next build"
9

This day and age you shouldn't need to install anything extra to implement multiple environment configuration! See GitHub repo NextJS template with config management

Next.js v9.4 and up has a more intuitive and ergonomic way for adding environment variables:

{
  "name": "package.json",
  "scripts": {
    "dev": "next dev",
    "build": "next build && next export",
    "build-dev": "TARGET_ENV=development next build && next export",
    "build-staging": "TARGET_ENV=staging next build && next export",
    "test": "jest --watch"
  }
}
{
  "name": "env.json",
  "development": {
    "APP_ENV": "development"
  },
  "production": {
    "APP_ENV": "production"
  },
  "staging": {
    "APP_ENV": "staging"
  },
  "test": {
    "APP_ENV": "test"
  }
}
// next.config.js v9.4+
const envConfig = require('./env.json')
const environment = process.env.TARGET_ENV || process.env.NODE_ENV

const nextConfig = {
  env: envConfig[environment], // getEnvConfig()
}
module.exports = nextConfig

function getEnvConfig() { // for multi-file config
  try {
    return require(`./env-${environment}.json`)
  } catch (err) {
    return require('./env.json')
  }
}
npm run dev # process.env.APP_ENV=development
npm run build # process.env.APP_ENV=production
npm run build-dev # process.env.APP_ENV=development
npm run build-staging # process.env.APP_ENV=staging
npm run test # process.env.APP_ENV=test

2 Comments

that doesn't make much sense to me in a container world. We shouldn't be building per-environment. The build should be only one and use ENV variables at run time, which is what ENV VARS are for, right?
hate it or leave it, today many organisations still using static site exports dumped in wherever random cloud bucket in production..
9

Short Answer

  1. Put your dev-env secrets in .env.dev
  2. Put your prod-env secrets in .env.prod
  3. Don't save secrets in .env.local. They will be brought there next.
  4. Add following to your package.json script.
"build-dev": "cp .env.dev .env.local && yarn build",
"build-prod": "cp .env.prod .env.local && yarn build"
  1. Upvote & Enjoy if it worked.

Additionally,

  1. You may want to add a variable APP_ENV.
  2. Set it to development and production based on file.
  3. Access process.env.APP_ENV based on your script invocation.

Long Answer

Well this is Mid-2023 and I wish there was a straightforward solution. Here's what works for me coming from React/vite to Nextjs with 2 different environments file.


Using Rodrigo's answer I first renamed

  • .env.development to .env.dev
  • .env.production to .env.prod

so that next.js doesn't pick them automatically during builds but they stay on local system.

If I don't do that then precedence kicks in and picks .env.production during deployment to my dev environment which I don't want.

Next I modified my scripts in package.json as

"build":"yarn build"

"predeploy": "cp .env.dev .env.local",
"deploy": "firebase use development && firebase hosting:channel:deploy dev",

"predeployprod": "cp .env.prod .env.local",
"deployprod": "firebase use production && firebase deploy -P production"

Read about Next JS Precedence Order briefly.

What this does is based on my "invocation" whether I want to deploy to dev/prod env it supplies the right secrets to the .env.local file.

For example, Say I want to deploy to dev. I run yarn deploy -> automatically runs yarn predeploy first setting my dev-secrets to .env.local. Secondly it runs the build.

You might wonder where's nextjs's build command? firebase deploy takes care of that and runs it behind the scene.

If you aren't using firebase following could suffice.

"build-dev": "cp .env.dev .env.local && yarn build",
"build-prod": "cp .env.prod .env.local && yarn build"

Originally answered here.

3 Comments

npm run build-dev gives "cp is not recognized as an internal or external command" in windows powershell but works in bash.
@abitcode you need to use a Linux compatible shell
Where do you tell different firebase projects to use different scripts to build or start?
4

npm i dotenv

Then add below code to next.config.js, restart the application and you are good to go!

// next.config.js

require('dotenv').config()
const webpack = require('webpack')

module.exports = {
  webpack: (config) => {
    config.plugins.push(
      new webpack.EnvironmentPlugin(process.env)
    )
    return config
  }
}

If your .env file is not located in same folder as next.config.js add path to your config like below,

require('dotenv').config({ path: 'path/to/.env' })

1 Comment

Adding the config.plugins.push line worked for me with Next 9.4.0; thanks!
4

For anyone interested to use .yml file for easy management of environment variables, here's how I did it.

Install a plugin yenv in devDependencies.

Add the below config in next.config.js :

const path = require("path");
const yenv = require("yenv");
const { PHASE_DEVELOPMENT_SERVER } = require("next/constants");

module.exports = (phase) => {
  const isDev = phase === PHASE_DEVELOPMENT_SERVER;
  const NEXT_ENV = isDev ? "development" : process.env.APP_ENV;
  const rawEnv = yenv(path.resolve(".env.yml"), { raw: true, env: NEXT_ENV });
  
  return {
    ...some other config,
    env: getEnvVars(rawEnv, isDev).raw,
    compress: true,
  };
};

function getEnvVars(rawEnv, dev) {
  const NEXT_PUBLIC = /^NEXT_PUBLIC_/i;
  const raw = Object.keys(rawEnv)
    .filter((key) => NEXT_PUBLIC.test(key))
    .reduce((env, key) => {
      env[key] = rawEnv[key];
      return env;
    }, {});
  // Stringify all values so we can feed into Webpack DefinePlugin
  const stringified = {
    "process.env": Object.keys(raw).reduce((env, key) => {
      env[key] = JSON.stringify(raw[key]);
      return env;
    }, {}),
  };
  return { raw, stringified };
}

Now just add different build commands based on environment in package.json scripts.

"scripts": {
    "dev": "node server.js",
    "build:production": "APP_ENV=production next build",
    "build:staging": "APP_ENV=staging next build",
    "start": "NODE_ENV=production node server.js"
  },

Now you can use your environment variables via a single file .env.yml like this :

base:
  NEXT_PUBLIC_SECRET_KEY : ""
  NEXT_PUBLIC_ANOTHER_SECRET: ""

development:
  ~compose: base
  NEXT_PUBLIC_SECRET_KEY: "bnbnfjf"

staging:
  ~compose: base
  NEXT_PUBLIC_SECRET_KEY: "absadsad"

production:
  ~compose: base
  NEXT_PUBLIC_SECRET_KEY: "lasjdasodsdsad"

Now you can call npm run build:production to load production env vars and npm run build:staging for staging env vars.

This provides the benefit of having any number of envs for your use case. You will just have to add a build command, and update env vars in .env.yml and you are good to go.

1 Comment

Thanks, this looks like a very elegant solution for me, compared to the others.
2

If you would like to use it without any 3rd party library you can expose it from the script directly with NEXT_PUBLIC_ at the start of the script, for example:

"scripts": {
  "start": "NEXT_PUBLIC_APP_ENV=development next dev"
}

than use it with

console.log(process.env.NEXT_PUBLIC_APP_ENV); // >>> development

1 Comment

I guess someone did not got it correct, or maybe version problems, but I am using it for many projects
1

You can use dotenv-cli and then set up different .env files for different environments in your package.json. Something like:

{
  ...
  "scripts": {
    "dev:production": "dotenv next", // Uses .env file
    "dev:staging": "dotenv -e .env.staging next" // Uses .env.staging file
  }
  ...
}

5 Comments

It's giving me an error of dotenv not found. Although I have installed it and working properly with local file
dotenv -e .env.development next build still loads production config
it still load the production env. I end up with just comment / uncomment variables inside the production env file. so ridiculous!!!
NODE_ENV determines if .env.development or .env.production file is loaded. You should use different env file names for your "application environment" by using .env.app.development and .env.app.production for example.
You need to add -- e.g. "dev:staging": "dotenv -e .env.staging -- next dev"
0

For those who still seek a solution for their faulty project, in my case my npm run dev script was "next dev src/" Getting rid of that immediately started loading .env files for me. Maybe give it a try.

A screenshot of my lovely fix

Comments

0

Here's my approach that first "unloads" the incorrect dotenv files and then loads the correct ones.

/* eslint @typescript-eslint/no-var-requires: 0 */
const  dotenv= require('dotenv')
const dotenvExpand = require('dotenv-expand')
const { PHASE_DEVELOPMENT_SERVER } = require('next/dist/shared/lib/constants')

module.exports = (phase) => {
  if (phase === PHASE_DEVELOPMENT_SERVER && process.env.ENVIRONMENT !== 'development') {
    unloadDevEnvVars()
    loadCorrectEnvVars()
  }

  return {}
}

function unloadDevEnvVars() {
  const developmentEnvVars = {}

  dotenv.config({ path: './.env.development.local', processEnv: developmentEnvVars })
  dotenv.config({ path: './.env.development', processEnv: developmentEnvVars  })

  Object.keys(developmentEnvVars).forEach(developmentEnvVarName => {
    delete process.env[developmentEnvVarName]
  })
}

function loadCorrectEnvVars() {
  dotenvExpand.expand(dotenv.config({ path: `./.env.${process.env.ENVIRONMENT}.local` }))
  dotenvExpand.expand(dotenv.config({ path: `./.env.${process.env.ENVIRONMENT}` }))
}

Comments

0

For those who don't want to use default nextjs env files you can use the parse method from dotenv.

Example:

next.config.mjs

import dotenv from 'dotenv'
import * as fs from 'fs'

const env = fs.readFileSync('path.to.your.custom.env.file')
const config = dotenv.parse(env)

/** @type {import('next').NextConfig} */
const nextConfig = {
  env: {
    ...config,
  }
}

export default nextConfig

In my case, my custom env file is used in CI/CD and some variables not supported by nextjs are needed like NODE_ENV

In this case you can delete the variable from the config object

next.config.mjs

import dotenv from 'dotenv'
import * as fs from 'fs'

const env = fs.readFileSync('path.to.your.custom.env.file')
const config = dotenv.parse(env)

delete config.NODE_ENV

/** @type {import('next').NextConfig} */
const nextConfig = {
  env: {
    ...config,
  }
}

export default nextConfig

Comments

0

One more solution. Less common, but works as well

#!/bin/sh

set -eu
set -o pipefail

app_mode=$1

if [ $app_mode != "development" ] &&
   [ $app_mode != "production" ]  &&
   [ $app_mode != "staging" ] 
then
  echo "Invalid app mode"
  exit 1
fi

export $(cat .env.${app_mode} | xargs)
yarn run next build 

And then just

./build.sh production

Also, nobody mentions that Node 20+ has loadEnvFile function So you can do something like this

"build": "next build"
"build:dev": "cross-env APP_MODE=development yarn build",
"build:prod": "cross-env APP_MODE=production yarn build",
"build:staging": "cross-env APP_MODE=staging yarn build"

And inside next.config.js

process.loadEnvFile(`.env.${process.env.APP_MODE}`);

Comments

-1

Next 9.4 support out of the box environment variables and included support for multiple environments.

New Environment Variables Support

  • Environment variables are only available in the Node.js environment by default
  • Environment variables prefixed with NEXT_PUBLIC_ are exposed to the browser

Exposing Environment Variables

Next.js allows you to expose variables using an environment variables file (.env), with included support for multiple environments. It works like this:

  • .env - Contains environment variables for all environments
  • .env.local - Local variable overrides for all environments
  • .env.[environment] - Environment variables for one environment. For example: .env.development
  • .env.[environment].local - Local variable overrides for one environment. For example: .env.development.local

2 Comments

but how can I start app that would use .env.staging?
Reason for downvote: This thread is concerned with being able to setup NextJS to run against multiple environments like : dev, qat, uat, staging, production - the out of the box environment variables do not support this - so copy/pasting this feature from the NextJS site doesn't really answer the quesiton

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.