Build Once, Deploy Anywhere for React

Continuous deployment and runtime configuration for static javascript applications

Build Once, Deploy Anywhere for React

Continuous deployment and runtime configurations for static javascript applications

Steps

Start by creating a public/config.js file in your project and adding config.js to your .gitignore. Create environment specific variables, for me they look like this:

// public/config.js

const apiUrl = 'localhost:1337';
const env = 'development';

The api url is obvious, but I use the env variable to display what environment my app is running in at the top of the page. It's also common practice to add a color for the purpose of easily identifying the environment.

Add the following lines to the head element in public/index.html:

<!-- public/index.html -->

<script src="%PUBLIC_URL%/config.js"></script>
<script>
  window.config = { apiUrl, env };
</script>

The first line grabs your your config file and the remaining lines make your config variables available to your react app. Just use window.config.apiUrl and window.config.env access the variables anywhere inside your app.

With this method we can easily change the config variables by altering the config.js which would live outside of your source control. It's very similar approach to how you would edit the configuration if you were building a 12 factor server side application.

There might be some concern about adding an extra http request for the config. For me this isn't an issue, but it is something to consider. To remedy this you could create a hash of the config file and append it to the filename similar to our javascript bundles, and cache the file on the client. For this to work it would require the public/index.html to be modified in the release, which is something I personally try to refrain from, but it can save an extra trip to your server for your end users.

If you're okay with modifying the html file in the release you might also consider just adding your config values to a script tag.

Although these could save your clients that extra trip all together it also means that to change the configuration you would need to create a new deployment.

Why?

Building once, deploying anywhere is an important principle that enables continuous deployment and easy deployments to cloud platforms. It describes the process of promoting a single artifact or package through testing environments and eventually production. This reduces the disparity between testing environments and production, one of the tenets of 12 factor applications

Angular and React docs specify building for each environment. This violates 12 factor and makes ci/cd less simple. I've spent weeks trying to solve this issue, created reddit threads, asked in software development facebook groups, etc. and no one really had a great answer for me.

My solution involves using a configuration file that is fetched with the initial html file. This differs from 12 factor guidelines regarding config management, but the issue is that these guidelines are really only relevant to server side applications as there are no environment variables in the browser. 12 factor's issue with config files are that secrets may accidentally be checked in to source control, but, again, this isn't relevant for static applications. There are no secrets on the client. Although I do believe we should refrain from checking in to source control just because the values do change between environments.

Since 12 factor config management doesn't account for static applications we can we can look at the reasons env vars are preferred.

The twelve-factor app stores config in environment variables (often shortened to env vars or env). Env vars are easy to change between deploys without changing any code; unlike config files, there is little chance of them being checked into the code repo accidentally; and unlike custom config files, or other config mechanisms such as Java System Properties, they are a language- and OS-agnostic standard.

They are easy to change between deploys... The other argument against config files that I haven't addressed is that they are language and OS agnostic, which, again is irrelevant because the only language used by the broswer is javascript.

This provides a simple way to develop runtime configurations for static applications. Although I use react in this example, it can be used for es6, vue, angular, etc.

Conclusion

This will allow us to deploy our single artifact to any environment. You can either create the public/config.js file in a release pipeline or leave it on the server, only updating when necessary. It really isn't that difficult to code, but runtime configurations are something I wish the major frontend framework maintainers would consider and help provide guidance or a solution that feels less hacky.