Dockerizing a React application provides numerous benefits, including simplified deployment, enhanced scalability, and improved consistency.
This blog post will guide you through the process of containerizing your React application using Docker, also show you the power of containers and unlock a world of possibilities for your development workflow.
Let's dive in and explore the prerequisites, steps, and best practices involved in Dockerizing your React application.
Prerequisite
To follow this tutorial, you will need to have :
- Installed Docker Desktop
- Basic knowledge of Docker CLI
- Installed npm
Set up the project
To create this project, you need to use npx
. If, for some reason, itβs not available, install or update it as the following:
$ npm install -g npx
npx makes it easy to install and manage dependencies hosted in npm registry. Itβs an npm package runner β helps to execute packages without installing explicitly.
Now, you can create a react app using npx:
$ npx create-react-app my-react-app
Enter in your project folder
$ cd my-react-app
Delete the node_modules
folder at the root of your project, you will not need it in your host environment because we will install dependencies during the build of the image, and we will use the node_modules folder of the container.
$ rm -rfv node_modules
Create the Dockerfile
Add a Dockerfile to the project root:
FROM node:16-alpine
# set working directory
WORKDIR /app
# add `/app/node_modules/.bin` to $PATH
ENV PATH="/app/node_modules/.bin:$PATH"
# install app dependencies
COPY package.json ./
COPY package-lock.json ./
RUN npm install
RUN npm install react-scripts@3.4.1 -g
# add project sources in /app
COPY . ./
# start app
CMD ["npm", "start"]
Create a .dockerignore
file and add these lines
node_modules
.dockerignore
This will speed up the Docker build process, as our local dependencies inside the node_modules
directory will not be sent to the Docker daemon.
Build and tag the Docker image:
$ docker build -f react.dockerfile -t react-app:latest .
Run the container
Once the build is done, you can run the container
$ docker run \\
-it \\
-d \\
--rm \\
-v ${PWD}:/app \\
-v /app/node_modules \\
-p 3000:3000 \\
-e CHOKIDAR_USEPOLLING=true \\
react-app:latest
volume: -v /app/package.json
Whatβs happening here?
- The
docker run
command creates and runs a new container instance from the image we just created. -it
starts the container in interactive mode.
Why is this necessary β As of version 3.4.1,react-scripts
exits after start-up (unless CI mode is specified) which will cause the container to exit. Thus the need for an interactive mode.-d
run the container in detach mode, i.e. in the background-rm
removes the container and volumes after the container exits.-v ${PWD}:/app
mounts the code into the container at /app.- Since we want to use the container version of the node_modules folder, we configured another volume:
-v /app/node_modules
. You should now be able to remove the local node_modules folder. -p 3000:3000
exposes port 3000 to other Docker containers on the same network (for inter-container communication) and port 3001 to the host.- Finally,
-e CHOKIDAR_USEPOLLING=true
enables a polling mechanism via chokidar (which wraps fs.watch, fs.watchFile, and fsevents) so that hot-reloading will work.
Open your browser to http://localhost:3000/ and you should see the app.
Try making a change to the App
component within your code editor. You should see the app hot-reload. β°
Using Docker Compose
A more common (cleaner) way to avoid using a long command to run a container, is to use docker compose. So add a docker-compose.yml
in your project root.
services:
react-app:
build:
context: .
dockerfile: react.dockerfile
image: simple-react-app:latest
container_name: rect-app
volumes:
- type: bind
source: .
target: /app
- /app/node_modules
ports:
- "3000:3000"
environment:
- CHOKIDAR_USEPOLLING=true
restart: unless-stopped
Without the anonymous volume /app/node_modules
, the node_modules directory in the container would be overwritten by the bind mounting of the host directory at runtime.
In other words, this would happen:
- BUILD STAGE - The node_modules directory is created in the image.
- RUN STAGE - The current directory is mounted (bind mounted) into the container, overwriting the node_modules that were installed during the build.
Anonymous volumes are not given an explicit name when they are first mounted into a container, so Docker gives them a random name that is guaranteed to be unique within your Docker host.
Besides the name, named and anonymous volumes behave in the same ways.
Build the image and fire up the container:
$ docker-compose up -d --build
Ensure the app is running in the browser and test hot-reloading again. β°
I hope you enjoyed reading this, and I'm curious to hear if this tutorial helped you. Please let me know your thoughts below in the comments. Don't forget to subscribe to my newsletter to avoid missing my upcoming blog posts.
You can also find me here LinkedIn β’ Twitter β’ GitHub or Medium
Wrap Up
In this tutorial, we've covered the prerequisites for Dockerizing a React application, the steps involved in creating a Dockerfile, and how to use Docker Compose to simplify the process of running your container.
You will find all the code snippets of this tutorial on my GitHub here.
By following these steps, you'll be well on your way to taking advantage of the many benefits that containerization has to offer.