DOCKERISED REACT APP CONFIG CHANGE WITHOUT RELOAD

Applicable only for non-sensitive configurations in a React App

Disclaimer: The content of this blog are my views and understanding of the topic. I do not intend to demean anything or anyone. I am only trying to share my views on the topic so that you will get a different thought process and angle to look at this topic.

     Docker environment variables which are passed from docker-compose.yml as .env file or as environment tag require a container reload if you change their value. However if there are certain app configuration which should not require a container reload then a simplest appraoch is to chose volume mounting. We will see this approach in detail in this blog.

Assumptions

Folder structure

Create an app named sample-app using create-react-app tool. Below is a sample folder structure for the same :

sample-app/

├── public/

│   └── index.html

├── config/

│   └── env.js

├── docker-compose.yml

├── Dockerfile

└── package.json

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

In the adjacent panel you can see a Dockerfile 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. 

Contents of env.js

const app_env = {

APP_VERSION: 1.2.3

};

In the app folder create a config folder and create a file named env.js inside that. Let us say it contains a configuration for APP_VERSION as 1.2.3.

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 an index.html file. Add a script tag for loading the env.js file as shown in the code snippet in the left panel.

Build and run using docker-compose.yml

version: '3'


services:

 sample-app:

   container_name: sample-app

   imagesample-app:local

   build: .

   ports:

     - 3001:80

   volumes:

     - ./config/env.js:/usr/share/nginx/html/env.js

In the left panel you can see the docker-compose.yml file which builds the image and runs the same inside the docker. The port binding shows that the app is running on your machine port number 3001 with URL http://localhost:3001. Volume mounting shows that ./config/env.js file is mounted inside the docker container at location /usr/share/nginx/html/env.js.

Note: Here it is important to mention the source volume mounting as relative location starting with a dot (Ex: ./config/env.js)

Accessing the environment variable in a React Component

import React from "react";

import logo from "./logo.svg";

import "./App.css";


interface AppEnv {

 [key: string]: any;

}

declare const app_env: AppEnv;


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

Now you can build and run a docker image for sample-app and see if the value is reflected on the UI. Try changing the value of APP_VERSION in the config folder on your system and refresh the page. The value should reflected on the UI unless the Javascript is cached. In that case try clearing the cache and refresh the page. Cautiously decide to place only those configurations in such way which are not sensitive but should not require container reload. A good example would be a feature flag.