Properly Setting Up React Development Environment Using Docker

Sudipto Baral
5 min readJul 22, 2021

Solving the node modules synchronization problem

It’s amazing to use just one command docker-compose up to start your development workflow in any host operating system. But it may become a daunting task to set up a perfect workflow especially when we have to sync our node_modules folder across the host machine and docker container.

In this blog, I will show how to properly set up the development and test containers of your react application. I will also show how to solve the empty node_modules folder problem in your host machine so that your linters do not yell at you anymore.

I’ve also written an angular version of this blog. If you are using Angular then follow this link.

Initial setup with Docker

Create a React project using the following command.

npx create-react-app dockerize-react

create a folder for saving docker-related files in the project root. I like to name the folder as .docker Your directory will look something like the following.

│ ├── ci
│ ├── dev
│ └── Dockerfile # our development dockerfile
│ ├── prod
│ └── scripts
└── ...

Create a Dockerfile in the .docker/dev/ directory.

This is a conventional docker file. We did the following in the Dockerfile

  • We took node:14 as our base image
  • Set the current working directory inside the docker container to app
  • We set the node environment variable.
  • Then we copy package.josn and package-lock.json
  • Install node modules using npm install
  • Finally, Copy everything from the current working directory of the host machine to the working directory inside the container ( which is app in this case ). This step is not necessary for the development Dockerfile though. We will map the host working directory to the containers working directory in the docker-compose.yml file anyway. But I like to keep this line here because in the future we may use this Dockerfile for production or take this Dockerfile as an inspiration to create the production Dockerfile. In that case, this line will work as a reminder that we need to copy the source code into the container.

Now let’s create the docker-compose.yml

Create a file name docker-compose.yml in the project's root directory. Following is my docker-compose.yml

  • we set the build context to the current folder .of the host machine
  • we provide the Dockerfile location which we want to use for building this (web) service.
  • we map the port so that we can access our react app running on port 3000 inside the container from outside i.e host machine.
  • The volume maps everything in the host machine to the app folder inside the container.
  • Here we map node_modules folder inside the container to the host machine. Remember we did npm install in the app folder inside the docker container. So we have a node_modules inside the docker container which we can map back to the host machine.

This is necessary because we want to actively develop our application while running the environment using docker. If we do not map node_modules from the container to the host then we will have to generate another node_modules locally so that our IDEs can work properly.

Also In our particular case, we are mapping everything inside the current context . to the app folder inside the container. If we do not map back node_modules from the container to the host then the host’s file will replace everything inside the containr's app folder. In this way, our node_modules folder generated earlier in the app folder inside the container will also get removed because we do not have any node module folder in our host machine. So we must map backnode_modules from the container to the host. Otherwise, our volume mapping.:/app will remove node_modules folder inside the container. Hence our setup will not work.

Now run docker-compose build and then docker-compose up to start the environment.

Now let’s quickly set up the test suit for our react app. We will use a different container for running our tests. Following is the docker-compose file with the test setup configuration added.

The Problem

There is a small problem with this setup. The node_mouldes folder is empty in the host machine. Our app will work perfectly because we have node_modules inside the docker container. That means synchronization did not happen properly.

As a result, different IDEs and code editors will give linting errors because they won’s find required node_modules locally to work with.

A simple workaround for this problem is to enter into that particular docker container and run npm install one more time.

First, delete the existing empty node_module folder. Then run the following command.

docker-compose exec web npm install

But I do not like running npm install again!!

I personally do not like to run npm install multiple times because it hinders smooth development workflow. My teammates want to just pull the repository and run docker-compose up --build and everything should work.

To solve this problem let’s modify the dockerfile a little bit.

Here we installed node_modules inside another temporary directory namely cache . Later we will copy this node_module to the app directory once the image is built.

Let’s modify the docker-compose.yml file and add commands to copy node_modules at the startup

with the cp -rfu /cache/node_modules/. /app/node_modules/ command, we copy node_modules from the temporary cache directory to the working directory after the image is built. The -u flag is important because we want to copy files only if anything is changed in the node_modules folder. In this way, we won’t copy files every time we run docker-compose up to start our development.

Development workflow for developers

Pull the source code

If pulled for the first time or the package.json changed run

docker-compose build

Then, run the following command to start the environment.

docker-compose up -d

To see the logs of your app

docker-compose logs -f web

To see and follow the logs of your tests

docker-compose logs -f test

If you need to install any npm package.

docker-compose exec web npm install pacakge-name

stop the containers

docker-compose down

Now developers can work smoothly without having to run npm install multiple times.

Here is the final code GitHub