Serving environment variables dynamically to your web app can be challenging. Here are two suggestions for tackling it properly.

Be it a public key for payments, Google Analytics ID, or other tokens and settings, there are client-side constants which deserve to be configurable. Developers usually utilize either REACT_APP_ prefix or Webpack DefinePlugin to enable such an option. However, both the approaches face a common drawback: they bake-in the values at compile-time.

If you are building small apps or your project is in the development stage, compile-time replacement might be a suitable option. It could also work well in case of build-specific configuration, e.g. to differentiate a development, test, or production build.

However, problems arise when you want to have a single build and deploy multiple instances, perhaps to show a ready-to-deploy version of the app to your customer while running another version in your production environment. In such a case you would want these two instances to have a slightly different configuration. That’s why you use environment variables in the first place, right?

Choose proper solution

One thing you can do is to read the configuration at runtime on server-side and then make these values available to the browser. You can import them statically on page-load by inserting a <script> tag into your index.html. Another possibility is to read them dynamically using XMLHttpRequest.

The former (<script> tag) lets you import the values with much less coding. It also loads the config in parallel with other javascript files. You may encounter issues when the variables are changed because HTTP caches resource files, meaning the browser may not see the new values right away. On the other hand, if you are replacing compile-time variables that would have been delivered as part of a script bundle, this shouldn’t be a problem since the caching behavior will be the same for your new dynamic variables.

The latter (XMLHttpRequest) gives you better control and is less likely to have issues with caching. You will need to code a little more. It employs XMLHttpRequest to fetch the variables meaning that the client-side code needs to account for the fact that the configuration will be loaded asynchronously.

Check out the sample project on GitHub

I’ve created a sample project that illustrates both approaches. The code of the sample project is available on GitHub. I have commented it extensively so don’t be afraid to explore the codebase for inspiration. No reverse engineering required.

The server side is implemented using Node.js and Express and the configuration is handled by Convict. There are two custom middlewares which publish the variables, either as JSON or a JavaScript file that initializes the configuration object. The client consists of a simple single-page app created with create-react-app that displays the values.

Security deserves special attention as it is common practice to put sensitive information such as passwords for third-party services into environment variables to avoid storing them in the repo. I strongly recommend that you validate which configuration variables are published to the client. You certainly don’t want to make your credentials or private keys available to the entire internet!

In conclusion, the main advantage of the approach I am proposing is that you can deploy several instances of a single build with different configurations, rather than having to create a separate build for each one.