How can we pass environment or config variables during run time in a cra(createreactapp). I do not want to build for different env but use one build with different configs in different env
How can we pass environment or config variables during run time in a cra(createreactapp). I do not want to build for different env but use one build with different configs in different env
You can add 2 environment variable files to the root of your project for development and production versions. They should be named;
.env.development - This will use npm start (dev stage) .env.production - This will use for the build (prod stage)
A property can be added as bellow, give attention to the prefix REACT_APP_
REACT_APP_PROPERTY1=some_value
And this property can be read as,
<p>{process.env.REACT_APP_PROPERTY1}</p>
We build our create-react apps as static websites and we push them directly to a simple web server. That's why you can't use env variables there. We found a nice workaround and we plan to write a short post about it:
1. Start your app with (one) environment variable
Let's assume you have a development, staging and production environment, like we do most of our projects.
We set just one ENV variable REACT_APP_ENV in our start up scripts. Each environment has it's own start and build script.
# package.json
# ...
"scripts": {
"start": "REACT_APP_ENV=development react-scripts start",
"start:staging": "REACT_APP_ENV=staging react-scripts start",
"start:prod": "REACT_APP_ENV=production react-scripts start",
"build:staging": "REACT_APP_ENV=staging react-scripts build",
"build:prod": "REACT_APP_ENV=production react-scripts build"
},
2. Setup config file
In your create-react app you store a config file under src/config/index.js. In this config file you can define the values based on the environment.
# src/config/index.js
const env = process.env.REACT_APP_ENV
export const appConfig = {
api: {
networkInterface: ({
development: 'http://localhost:5000/graphql',
staging: 'https://project-staging.herokuapp.com/graphql',
production: 'https://project.herokuapp.com/graphql',
})[env],
// add more here
},
}
export default appConfig
3. Usage
In the app you access the config simple like this:
import config from './src/config'
# usage
config.api.networkInterface
I had the same question a couple of days ago and found a hacky, yet a potential solution. Basically, you use a config.js inside your /build/public directory. The config.js could be a simple code like this:-
window.DYNAMIC_PROPERTIES = {
"ENV": "UAT",
"API_ENDPOINT": "UAT"
}
You want this particular file to be a dynamically generated one (based on your environment). If you are interested in how I achieved this, read on till the end.
Now, to access this in react application, include this script in your index.html (head tags).
<script src="%PUBLIC_URL%/configs.js"></script>
Once this is done, you should be able to access the env variable dynamically in your application like this.
const envValue = window.DYNAMIC_PROPERTIES.ENV
const apiEndpoint = window.DYNAMIC_PROPERTIES.API_ENDPOINT
The basic idea here is that the HTML will execute the script in your build/public folder. This script will attach properties to the window scope and make them available to your application.
Now the whole point of doing all this is to be able to dynamically inject the environment variables. Here's how I achieved it:-
We use an nodejs (express) server to serve the UI, so I managed to dynamically create this simple js file in the /build/public/ folder. Sample code for the server.js:-
const path = require("path");
const express = require("express");
const fs = require('fs');
const app = express(); // create express app
fs.copyFile(`config.${process.env.ENVIRONMENT}.js`, "./build/config.js", (err) => {
if(err){console.log("Something went wrong while setting env variables", err)}
else{console.log("Env variables set for ",process.env.ENVIRONMENT )}
});
// add middlewares
app.use(express.static(path.join(__dirname, ".", "build")));
app.use(express.static("public"));
app.use((req, res, next) => {
res.sendFile(path.join(__dirname, ".", "build", "index.html"));
});
// start express server on port 5000
app.listen(5000, () => {
console.log("server started on port 5000");
});
Basically, I have separate config files like config.dev.js, config.uat.js and so on. The start command for this server would inject a property "ENVIRONMENT" as either dev or uat or prod and this node server will place the correct config files in the build/public at application startup (notice the fs.copyFile command).
Ok, hope this helps. If you find a better way to do this, do comment your solution.