Most of the time using custom build time environment variables are suitable but runtime variables can be valuable in addition to or in place of environment variables.
Runtime configurations or variables, can be beneficial when you need to change a configuration at run time. Some examples of variables you could load at runtime is theme data or api urls.
Do not store any secrets (such as private API keys) in your environment or runtime variables!
Why runtime variables?
- Build app only once and deploy the same build to multiple environments (i.e. dev, qa, prod)
- Configurations can be changed/updated after the app is deployed
- Load configurations from an external source
- Save time - Build pipelines can take long periods of time some with many steps (i.e. infrastructure steps, build steps, tests, etc). Loading configurations during runtime can help reduce the amount of times your app is built.
How do runtime variables work?
Runtime variables get loaded into the browser's window object when the index.html of a single page application gets loaded.
In the <head>
or <body>
of the html file we can run a Javascript snippet that sets up the configurations on the window object.
Javascript code in the index.html file will get loaded synchronously from top to bottom as long as the async
flag is not present. By placing the code to load our snippet before the main bundle it can be guaranteed that the values will be set on the window object and accessible when your main javascript bundle is loaded.
Let’s look at how this can be accomplished.
Create a new file called runtime-config.js and setup some variables
// runtime-config.js
window['runConfig'] = {
apiUrl: 'test.com/api'
}
To load the script in the index.html file add the script tag to the file inside either the <head>
or <body>
tag. If you are starting with Create React App the file will be located at <project_root>/public/index.html
<script src="%PUBLIC_URL%/runtime-config.js"></script>
// index.html
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<!-- runtime config -->
<script src="%PUBLIC_URL%/runtime-config.js"></script>
<!-- runtime config -->
<title>React App</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
</body>
</html>
How to use in a React component
To access the variables that are setup in the runtime-config file, from your app files you can reference the window object.
window['runConfig']
Example usage in a React component:
export const MyComponent = () => {
const { apiUrl } = window['runConfig'];
return (
<div>Runtime config apiUrl: {apiUrl}</div>
)
}
Full Example:
https://github.com/mattcat10/react-runtime-config
Notes:
Don't use runtime configurations if your configurations are really large. They are loaded synchronously and could slow down your initial load time of your app
This post explains how to use runtime configurations in a react app, but the same concept can be applied to any SPA framework.
Top comments (10)
Thanks for the great article! Finally, after days of headache, it helped me understand the things about "build once, deploy everywhere".
Just one thing though, as @idjuraj suggested below, on making variables specific per env, you said
during the deployment pipeline as a step, calling a bash script to read in the proper environment json file and create the runtime-config.js file.
, can this be also applied to when we push the code to pipleine? I mean, I have a git pipeline that automates the build process as soon as the code is pushed. So is there any way that I can define the environment into that pipeline (Inside the yml file) ? So that build can be created automatically for different envs?Can you help me how to configure for dev, prod and staging at the runtime and how BUILD_ENV get the environment variable
ENV_CONFIG = ${node -p -e "JSON.stringify(require('./${CONFIG_BASE_PATH}/${BUILD_ENV}.config.json'))")
thanks for the article !!, I only have one question I tried this methode but had a small issue. when I open a new tab (when clicking a link for example) the scripts doesn' get executed and would have undefined error when trying to read the config object.
Solved my issue. Thanks!
Thanks Man!
This is exactly what I needed!
Great article! Do you have any advice on how to make these variables specific per environment?
Thanks for the reply. Considering that these are runtime configs, one method you can use is creating the
runtime-config.js
file as part of the deployment pipeline.I like to keep the config files per environment organized in json in my repository, so one method that I like to use is to have a different json file per environment (dev.config.json, stg.config.json, prod.config.json) making sure the file name is the same as the environment name and keeping the property names consistent
Ex dev json:
Ex prod json:
Then during the deployment pipeline as a step, calling a bash script to read in the proper environment json file and create the
runtime-config.js
file.The bash script looks something like this:
Calling it like this during your deployment pipeline
BUILD_ENV=stg ./config-expansion.sh
I am sure you can find other ways to generate the run-config.js file but this is one method that has worked for me in the past.
Is there any way, to use this same configuration for local setup? This worked for runtime but localhost could not find them!
Hey,
If you are using webpack, in your local webpack configuration you can create the file using the create-file-webpack plugin (npmjs.com/package/create-file-webpack)
Then your plugin configuration would look something like this:
And if you wanted to load your config from a file you can import it like:
and replace
{ apiUrl: 'test.com/api'}
withmyConfigVar
Does anybody have tips on testing this with Jest?