Create React App: Runtime vs Buildtime Environment Variables
This post will show you how I migrate create-react-app custom environment variables from buildtime to be configurable at runtime.
According to create-react-app docs, we can not embed environement variables during runtime.
so defining them with the prefix REACT_APP_
will expose them only in build-time where you can access them by process.env.REACT_APP_KEY
Note: I will use these aliases throughout the rest of the article:
- env vars instead of environment variables
- env/s instead of enviroment/s
Initializing Environment Variables in Buildtime
To define env vars in buildtime, just put your own env vars keys into the .env
file located in your home path, and prefixed by REACT_APP_
.
The env vars in this file will be exposed to your JS code in the development environment only.
There are more configurable ways to define env vars in test, production envs, and even override all the three envs
locally on your machine as described in create-react-app docs as the following:
- test env > add the env vars in
.env.test
- prodcution env > add the env vars in
.env.production
- locally-test-override env > add env vars in
.env.local.test
- locally-production-override > add env vars in
.env.local.production
- locally-development-override > add env vars in
.env.local.development
There is something to be considered; .env
files have precedence rules based on which npm/yarn command you will run as the following:
yarn/npm start
:.env.development.local
,.env.development
,.env.local
,.env
yarn/npm build
:.env.production.local
,.env.prodcution
,.env.local
,.env
yarn/npm test
:.env.test.local
,.env.test
,.env
when the precedence is from the left to the right.
Note: .env.local
is missing from yarn/npm test
and don't know why! but it is mentioned in create-react-app docs!
A final example will be a .env
file with env vars keys and values like this:
REACT_APP_KEY = <some-value>;
Migrate to Runtime Environment Variables
This is a hack solution because the definition of env vars in browsers does not even exist. It is something like a fake abstraction as mentioned here.
We will keep our .env
file only with the default development env vars as it is our main concern.
How this will be done?
I extract the env vars in .env
file into a new javascript config file using a simple bash script as below imported from here
#!/bin/bashset -e# Define env-config pathenv_config_path=./env-config.js# Recreate config filerm -rf $env_config_pathtouch $env_config_path# Add assignmentecho "window._env_ = {" >> $env_config_path# Read each line in .env file# Each line represents key=value pairswhile read -r line || [[ -n "$line" ]];do# Split env variables by character `=`if printf '%s\n' "$line" | grep -q -e '='; thenvarname=$(printf '%s\n' "$line" | sed -e 's/=.*//')varvalue=$(printf '%s\n' "$line" | sed -e 's/^[^=]*=//')fi# Read value of current variable if exists as Environment variablevalue=$(printf '%s\n' "${!varname}")# Otherwise use value from .env file[[ -z $value ]] && value=${varvalue}# Append configuration property to JS fileecho " $varname: \"$value\"," >> $env_config_pathdone < .envecho "}" >> $env_config_path
This will create as env-config.js
file that you will need to embed like below:
<script src="%PUBLIC_URL%/env-config.js"></script>
into your public/index.html
.
And, this is how the file will look like:
window.__env__ = {REACT_APP_KEY: value,};
Putting All Together
To get all of these running in development, you will need to:
• update yarn/npm start
command to
./env.sh && ./env.sh && cp env-config.js ./public/ && react-scripts start
• replace all
process.env.REACT_APP_KEY;
by
window.__env__.REACT_APP_KEY;
Setup Tests Environment Variables
Doing all of the above will not guarantee running your test suites correctly, actually, you will see an error
that can't read property REACT_APP_KEY
of undefined. This is because __env__
is not exposed in the test environment which by the way is a build-time process. There is a hack solution
for this problem which is reusing .env
file to assign all the values from it to __env__
into your setupTests.js
like below:
window.__env__ = {REACT_APP_KEY: process.env.REACT_APP_KEY,};