With the popularity of cloud computing, containerization is a common technique used for mid to large scale project because it gives us isolated, scalable and easy way to deploy our application.
Today we are going to learn and create a production-ready docker image for React.js application. I really exited with this one so, let's start it out 😼.
Requirement
First, ensure Docker is installed on your machine. If you haven't please install it and come here again.
docker -v
Then, ensure Node.js is also installed on your machine. We need this to bootstrap our React.js application.
node -v
Initialize a Project
Theres so many ways to intializing React.js application. Either manually, pre-built template or front-end tools.
In this article we are going to use front-end tool called Vite because it's fast and configurable.
HINT
You may use Create React App (CRA) to intialize your application. It's also good and developed by Facebook Open Source too.
Open your terminal and type the following command.
npm create vite@latest docker-production-react
A prompt will appear. Just select React and JavaScript.
Now move to folder docker-production-react
and run the following command.
npm i
HINT
npm i
is just aliased command fornpm install
.
To ensure the initialize process works fine, let's start the local server.
npm run dev
Open with your browser http://localhost:5173
and you should see something like this.
Create Custom Docker Image
In this article, we'll using a technique called multi-stage build. Our goals is to separate the build process of React.js application itself and the web server (to serve the website).
Setup For Build Process
First, inside docker-production-react
create a Dockerfile
file and follow snippet below.
Here, we are using 16.17.1-alpine3.16
. I choose alpine linux because it's tiny so, it make build process faster.
FROM node:16.17.1-alpine3.16 as build
WORKDIR /usr/app
COPY . /usr/app
RUN npm ci
RUN npm run build
The snippet above tells Docker to pull (when hasn't pulled) or use pulled image from Docker Hub to use node:16.17.1-alpine3.16
as the base image for build stage, set the current directory to /usr/app
, running npm ci
and finally build the application using npm run build
command.
Setup For Web Server
Because React.js application is a static files, we need a simple, yet performant we web server in order the user able to access the application. Therefor I choose Nginx to handle this job.
From the previous Dockerfile
, follow this snippet.
FROM nginx:1.23.1-alpine
EXPOSE 80
COPY ./docker/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf
COPY --from=build /usr/app/dist /usr/share/nginx/html
Here we tell Docker to use nginx:1.23.1-alpine
as the base image and exposing port 80 (default port Nginx), copy the configuration file and copy our bundled application.
HINT
Because Nginx act as a common web server and only serve our application, it's OK to not bind the configuration file at runtime.
For the Nginx configuration file, create default.conf
file inside docker/nginx/conf.d
.
mkdir -p docker/nginx/conf.d; touch docker/nginx/conf.d/default.conf
...and follow below snippet.
server {
listen 80;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
}
The configuration just serve our application, but you can customize it as your needs.
Here our final Dockerfile
...
FROM node:16.17.1-alpine3.16 as build
WORKDIR /usr/app
COPY . /usr/app
RUN npm ci
RUN npm run build
FROM nginx:1.23.1-alpine
EXPOSE 80
COPY ./docker/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf
COPY --from=build /usr/app/dist /usr/share/nginx/html
.dockerignore
It's similar with .gitignore
, but it's used when you build your Docker image.
touch .dockerignore
Put it inside .dockerignore
.git
.DS_Store
.env
node_modules
IMPORTANT
Never put sensitive information inside your Docker image. Exclude it using
.dockerignore
.
Build Phase
Execute this command to build our image.
docker build -t web:0.1.0 .
Ensure the image is available.
docker image ls
Testing
Let's verify our works.
docker run --rm --name web-1 -p 80:80 -d web:0.1.0
Now open http://localhost
with your browser and you should see something like this.
Yeay! It's working...
Bonus: The Source Code
Find it here.
Also follow my latest experiment.
Top comments (5)
It depends to what ecosystem you are working. If you use something like Netlify, Vercel, etc that gives you ability to build and deploy the website on the fly, so skip this article. But if you fully using something like K8s, this article gives you an insight how to build that image to deploy later.
Docker has no limitation on how you to use it. It just a simple software that wrap your application inside OS level. That means you can deploy your application to any machine as long as it has Docker runtime.
I can't imagine if I need to setup Nginx, Node, etc just to deploy React apps to new machine. Meanwhile, with docker, you just need to run the container.
React is just static file, man. The main reason of Dockerize it to take benefits of isolated, easy to deploy and scalability. And again, use a vendor lock if you wont Dockerize it. But, under the hood those vendor does the same thing.
Why limit to React? This question sounds like why you only eat burger even theres sandwich and hotdog?. Yeah, in this context, React.js is my daily use.
npm run build
builds an app to thebuild
folder, so it should be:COPY --from=build /usr/app/build /usr/share/nginx/html
or am I missing something?
I use vite in this project. So, the build files is in
dist
instead ofbuild
except you use CRA.I appreciate your writing. And this article is valuable for beginners of Dockerization.
Someone might say no, but I say YES.
Dog barks, train goes.