DEV Community

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

Posted on • Edited 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. Install react-inject-env


npm install react-inject-env --save-dev
yarn add react-inject-env --dev


Enter fullscreen mode Exit fullscreen mode


2. Add the following to index.html



<script src='/env.js'></script>


Enter fullscreen mode Exit fullscreen mode


3. 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


4. 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


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


6. 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_MAIN_TEXT="Black Background" npx react-inject-env set

# with a blue background
REACT_APP_COLOR=blue REACT_APP_MAIN_TEXT="Blue Background" npx react-inject-env set

# for windows
set REACT_APP_COLOR=navy&& set REACT_APP_MAIN_TEXT=Navy Background&& npx react-inject-env set


Enter fullscreen mode Exit fullscreen mode


7. 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 #6, 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:

Top comments (19)

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
 
eslynn profile image
lynn • Edited

Hello! Sorry for the late reply. I've updated the instructions to show how to use it with Windows!

# for windows
set REACT_APP_COLOR=navy&& set REACT_APP_TEXT=Navy Background&& npx react-inject-env set
Enter fullscreen mode Exit fullscreen mode
Collapse
 
yasintaab profile image
yass🍉

Hello, where must i put this command bash at windows? is that inside folder code? or inside build output folder?

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

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
 
melanie45 profile image
MelanieHudson

instructions are not that clear.
if you want to add script file to the index.html that means it is located at public folder which is outside of src directory and you can't import something that falls outside of src folder.

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 • Edited

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

Collapse
 
eslynn profile image
lynn

Nice! Thanks for the feedback!

Collapse
 
bjprocks profile image
bjp-rocks

Hi I have installed the below package

Image description

For my React application I need to give the application url after I generate build using npm run-script build

so to statisfiy this need I am creating REACT_APP_HOST_IP environment variable and passing it through below command as suggested

REACT_APP_HOST_IP=X.X.X.X npx react-inject-env set

output

** Setting the following environment variables:
{ REACT_APP_HOST_IP: 'X.X.X.X' }
**

Below is the output of my env.js file present inside build folder.

_window.env = {
"REACT_APP_HOST_IP": "X.X.X.X"
}
_

error I am getting on browser console is below

GET http://{application-url}:8084/env.js net::ERR_ABORTED 404 (NOT FOUND)

console.log output is undefined of the script where I am using the value of REACT_APP_HOST_IP

Image description

So, in my application where I suppose to use this environment variable value it is showing me _undefined _ on console.log

thank you for creating this package but would please help me to solve the issue described above?

Collapse
 
vitalykolesnikov profile image
Vitaly Kolesnikov

Have you managed to fix that? I have the same problem now...

Collapse
 
bhushanpatilofficial profile image
bhushan-patil-official • Edited

Hey, First of all great post thanks for your insight and sharing of your knowledge with all of us. I was wondering, isn't this a security risk to be able to change or have all the env variables on the client browser in env.js? Some can change things and break application and might extract unwanted keys and leaks using the application?

Collapse
 
eslynn profile image
lynn

Fundamentally, there's no difference in security. Whether you store the environment variables in env.js or whether you bundle it together with your .js code, the variables are still stored on the client side and can still be mined or modified by the user. Secrets should not be stored on the client to begin with.

Storing it in this manner does make it a lot easier for someone to mine or change the data though! If this is a concern then you could employ other techniques such as obfuscation.

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 • Edited

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.

Collapse
 
yeldev profile image
Yelyzar Kaiukov

Hello. Very helpful post, thank you.
Could you please provide solution, how to use env variables for development mode with that approach?

Collapse
 
yeldev profile image
Yelyzar Kaiukov • Edited

I tried this solution for development
It is works for development mode, but I receive this kind of error
"Uncaught SyntaxError: Unexpected token '<'"

dev-to-uploads.s3.amazonaws.com/up...
dev-to-uploads.s3.amazonaws.com/up...

declare global {
interface Window {
env: any
}
}

type EnvType = {
REACT_APP_HOST_IP_ADDRESS: string,
}
const DEFAULT_DEVELOPMENt_ENVS = {
...process.env,
REACT_APP_HOST_IP_ADDRESS: "localhost:7777"
}

const generateEnv = (): EnvType => {
if (process.env.NODE_ENV !== "production") {
return DEFAULT_DEVELOPMENt_ENVS;
}
return { ...process.env, ...window.env }
}

export const env = generateEnv();

Collapse
 
vitalykolesnikov profile image
Vitaly Kolesnikov

I have the same problem. Did you solve it?

Collapse
 
gdiazderadaa profile image
Germán Díaz de Rada

Facing the same issue. Any solutions?