REACT PROJECT
DYNAMIC ENV VARIABLES
Building docker image for React project
A react app created using create-react-app framework follows a convention to resolve the environment variables during the build phase. The create-react-app achieves that using dot-env node package. Some convention in naming the environment config files is followed. A typical environment setup has three kinds development, staging and production. If you build an image and test it in development environment then the same image should be used for staging. Once tested in staging the same should be deployed in production. What changes with environment is environment specific settings or configurations. We should not rebuild an application again. Also if there is any BUG spotted in production environment and we want to replicate the same on the developer machine then we should take the production image and run on local with local configuration.
Assumptions
The app in run inside docker on nginx
There is no separate web-package configuration and we are using the app as it is setup by create-react-app
Folder structure
Consider if you created an app named "sample-react-project" then your folder structure would look something like this :
sample-react-project/
├── docker-entrypoint.d/
│ └── 99-generate-env-js.sh
├── Dockerfile
├── docker-compose.yml
├── .env
├── package.json
└── ...
Add reference of env.js in index.html
<!DOCTYPE html>
<html lang="en">
<head>
<script src="env.js"></script>
</head>
<body>
The body content is added here....
</body>
</html>
In the create react app project inside the public folder we have index.html file. Add a script tag for loading the env.js file as shown in the code snippet in the left panel.
Contents of env.js
const app_env = {
APP_VERSION: 1.2.3
};
You can name a global constant like app_env or any desired name.
Note: This is how this file will be generated by using the values passed by docker-compose.yml environment variables. You don't need to create this file manually. The script 99-generate-env-js.sh will do it for you.
Create a script to generate env.js
#!/bin/bash
ENVJS=/usr/share/nginx/html/env.js
echo "const app_env = { " > $ENVJS
ENV_KEYS=( \
"APP_VERSION" \
)
for key in "${ENV_KEYS[@]}"
do
varname=$key
value=$(printf '%s\n' "${!varname}")
echo " $varname: \"$value\"," >> $ENVJS
done
echo "};" >> $ENVJS
The script will generate the env.js file by reading the list of ENV_KEYS from the environment variables passed on by Docker container. We create this file at the location /usr/share/nginx/html inside the docker container. The script name should be 99-generate-env-js.sh. The integer prefix represents the order of the execution of the entrypoint scripts.
Place this script inside the folder docker-entrypoint.d of your react project
Docker environment variables can be accessed directly in a bash script which is executed as entrypoint script of the docker container.
Create a Dockerfile for the project
FROM node:18-alpine as builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
FROM nginx:alpine as server
COPY --from=builder /app/build /usr/share/nginx/html
RUN apk add bash
COPY docker-entrypoint.d/* docker-entrypoint.d/
RUN chmod +x docker-entrypoint.d/99-generate-env-js.sh
In the left panel you can see the docker file code for dockerizing this react app. It is a two stage docker build. In the first stage we build the project by copying all the source code inside the docker. It generates a build folder as per the output of the npm run build command from react scripts. In the second stage we copy the build folder from the first stage and run that on nginx image as server.
Note that we are copying the docker-entrypoint.d folder from stage one to stage two of the docker build and assigning the executable permission to the script inside the folder.
Sample docker-compose.yml which will build and run this image
version: "3"
services:
sample-react-project:
container_name: sample-react-project
image: sample-react-project:local
ports:
- 3000:80
build: .
env_file:
- env-local
In the left panel you can see the docker-compose.yml file which builds the image and runs the same inside the docker. We are using an env file called env-local. Your machine port would be 3000 for running this app and URL would be http://localhost:3000.
Accessing the environment variable in a React Component
import React from "react";
import logo from "./logo.svg";
import "./App.css";
declare const app_env: any;
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.tsx</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React {app_env.APP_VERSION}
</a>
</header>
</div>
);
}
export default App;
You can access this environment variable in a react component as shown in the left panel code. If you are using typescript template for generating the app then you will need to declare a placeholder variable in this component like declare const app_env: any; as shown in the code.
Summary
In this setup you can now build a docker image for the project and run the same image on different environments by changing the .env file or the environment variables passed to the service declaration inside the docker compose file.