DEV Community

Cover image for How to dynamically change your React Environment variables without re-building
lynn
lynn

Posted on

How to dynamically change your React Environment variables without re-building

Have you ever needed to deploy a React application to multiple environments?

If you've ever had to do the above, then this scenario may sound familiar to you: You already have an existing build file, you wish to change some environment variables, then re-deploy it to a new environment.

Unfortunately, you soon realize that the environment variables have been hard-coded within the build file. This means that you cannot change the environment variables without re-building.

Are there any other ways to modify environment variables?

We asked around to see if the other projects had a better way to handle it. It turned out that different teams had their own approach:

  • Some teams simply re-built it for each environment
  • Some teams fetched the environment variables asynchronously from an external data source
  • One team stored all the environment variables in the app, then selected the environment based on the URL
  • One team stored a placeholder environment variable, then replaced it before serving the file

What would an ideal flow look like?

All of the above had certain tradeoffs that we weren't willing to make. We were looking to build something that fulfilled the following:

  1. Does not require a rebuild
  2. Minimal code change required
  3. Allows synchronous access of environment variables
  4. Easy to integrate with your current workflow
  5. Simple and straightforward

react-inject-env: a tool that allows you to modify environment variables after the static file has been built

The short and simple explanation is that it creates an env.js file in the /public folder. The file is then executed at the start and the variables are loaded into the window object.

Here's how to use it

  1. Add the following to index.html
<script src='/env.js'></script>
Enter fullscreen mode Exit fullscreen mode


2. Create a new file called env.js and copy the following code:

export const env = { ...process.env, ...window['env'] }
Enter fullscreen mode Exit fullscreen mode


3. Replace all instances of process.env with the newly created env variable

import { env } from './env'

export const App = () => {
return (
  <div style={{backgroundColor: env.REACT_APP_COLOR}}>
    <span>{env.REACT_APP_MAIN_TEXT}</span>
  </div>
  )
}
Enter fullscreen mode Exit fullscreen mode


4. Build your static files using npm run build / react-scripts build / whatever your build script is.


5. Set your environment variables using the following command:

[env variables] npx react-inject-env set

# with a black background
REACT_APP_COLOR=black REACT_APP_TEXT="Black Background" npx react-inject-env set

# with a blue background
REACT_APP_COLOR=blue REACT_APP_TEXT="Blue Background" npx react-inject-env set
Enter fullscreen mode Exit fullscreen mode


6. Open build/env.js to verify that your variables are present.

These variables will then be read by your app at runtime.

build/env.js screenshot sample

And that's it, you're done!

If you need to modify your environment variables again, you can either (a) repeat Step #5, or (b) modify build/env.js directly.


What about <insert tool>?

This utility was built to be as simple as possible, so it should integrate and work well with most other tools.

.env / dotenv

react-inject-env will automatically detect environment variables in your .env file located in your root folder.

Note: Environment variables passed in through the command line will take precedence over .env variables.

Typescript

In Step #2, create a file named env.ts instead of env.js

declare global {
  interface Window {
    env: any
  }
}

// change with your own variables
type EnvType = {
  REACT_APP_COLOR: string,
  REACT_APP_MAIN_TEXT: string,
  REACT_APP_LINK_URL: string,
  REACT_APP_LOGO_URL: string
}
export const env: EnvType = { ...process.env, ...window.env }
Enter fullscreen mode Exit fullscreen mode

Docker

react-inject-env can also be neatly integrated with Docker

FROM node:16.10-slim
COPY . /app
WORKDIR /app

RUN npm install
RUN npm run build

EXPOSE 8080

ENTRYPOINT npx react-inject-env set && npx http-server build
Enter fullscreen mode Exit fullscreen mode
docker build . -t react-inject-env-sample-v2
Enter fullscreen mode Exit fullscreen mode
docker run -p 8080:8080 \                   
-e REACT_APP_COLOR=yellow \
-e REACT_APP_LOGO_URL=./logo512.png \
-e REACT_APP_MAIN_TEXT="docker text" \
-e REACT_APP_LINK_URL=https://docker.link \
react-inject-env-sample-v2
Enter fullscreen mode Exit fullscreen mode

Summary

This package has greatly enhanced our workflow and has cut our build and deployment times by over 10 minutes. In the past, we had to build 4x times - once for each environment - but now we only need to build it 1x. Deployments are blazing fast now!

It was built with simplicity in mind, so regardless of which tool you are using, you should be able to integrate it as part of your workflow!

Links

For more detailed information and support and samples, you may check out the following links:

Discussion (9)

Collapse
lukeshiru profile image
LUKESHIRU

I recommend you use globalThis instead of window, which is actually more "global" than window. More data here.

Collapse
eslynn profile image
lynn Author

Nice! Thanks for the feedback!

Collapse
Sloan, the sloth mascot
Comment deleted
Collapse
eslynn profile image
lynn Author • Edited on

Hey! Have you tried the sample code yet? Does it work for you?

Alternatively you can upload a sample of your code on GitHub and I'll try to take a look at it!

Collapse
ledpup profile image
ledpup

That npx command you run - npx react-inject-env set - with the environment variables before the command, e.g., REACT_APP_COLOR=black REACT_APP_TEXT="Black Background" npx react-inject-env set...

Is that a Bash command? Can this be reworked into a Windows cmd? I've tried a few ways but don't see how to do it.

Collapse
adrianfleming profile image
AdrianFleming

Thanks for this, I've always used Webpack externals and have been tasked with finding an elegant solution to injecting variables at runtime, and this is better than creating an endpoint to return the variables or loading a Json file. I have a question though; my app was using cache busting in Webpack but the npx script creates a new env.js in this case. I've switched cache busting off for now but is there any way the script can pick up the cache busting file, in my case env.5df60b24c6fb4378557c.js?

Collapse
eslynn profile image
lynn Author • Edited on

Hey! I'm not familiar with cache-busting on webpack as I typically use CRA. Do you think you could provide a sample?

Collapse
elugens profile image
Stacey Wilson

What are the security implications when you set the dot env in an external script, are we risking any exposure to outside actors.

Collapse
eslynn profile image
lynn Author • Edited on

Good question! The biggest risk is that the current instructions provided does not lock it to any specific version. This means that if my npm account was compromised by a malicious attacker, they could add in additional line of code that sends all the data to a remote server. They could then publish a new version on npm, and everyone will execute the new malicious code instead.

To guard yourself against this, you can lock the version by installing and specifying it in your package.json. This will ensure that you will always run the same version of the script, where the source code can be verified at GitHub. This is good practice in general anyway, so I'll be editing the article to add instructions on how to do this.

On a side note, React webpages are typically public and client-side, which means that they are typically not able to guard secrets. In general, they should not have any sensitive information being passed to them at build time.