DEV Community

Cover image for Dockerize Production React.js
M. Akbar Nugroho
M. Akbar Nugroho

Posted on

Dockerize Production React.js

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
Enter fullscreen mode Exit fullscreen mode

Then, ensure Node.js is also installed on your machine. We need this to bootstrap our React.js application.

node -v
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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 for npm install.

To ensure the initialize process works fine, let's start the local server.

npm run dev
Enter fullscreen mode Exit fullscreen mode

Open with your browser http://localhost:5173 and you should see something like this.

Vite + React

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

...and follow below snippet.

server {
    listen 80;
    root /usr/share/nginx/html;
    index index.html;

    location / {
        try_files $uri $uri/ /index.html;
    }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

.dockerignore

It's similar with .gitignore, but it's used when you build your Docker image.

touch .dockerignore
Enter fullscreen mode Exit fullscreen mode

Put it inside .dockerignore

.git
.DS_Store
.env
node_modules
Enter fullscreen mode Exit fullscreen mode

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 .
Enter fullscreen mode Exit fullscreen mode

Ensure the image is available.

docker image ls
Enter fullscreen mode Exit fullscreen mode

Testing

Let's verify our works.

docker run --rm --name web-1 -p 80:80 -d web:0.1.0
Enter fullscreen mode Exit fullscreen mode

Now open http://localhost with your browser and you should see something like this.

Vite + React

Yeay! It's working...

Bonus: The Source Code

Find it here.

Also follow my latest experiment.

GitHub logo thexdev / devlabs

🧙‍♂️ Find my latest experiment here! 🪄

Top comments (5)

Collapse
 
thexdev profile image
M. Akbar Nugroho

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.

 
thexdev profile image
M. Akbar Nugroho • Edited

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.

Collapse
 
stanleykaleta profile image
Stanislav Kaleta • Edited

npm run build builds an app to the build folder, so it should be:
COPY --from=build /usr/app/build /usr/share/nginx/html
or am I missing something?

Collapse
 
thexdev profile image
M. Akbar Nugroho

I use vite in this project. So, the build files is in dist instead of build except you use CRA.

Collapse
 
railsjack profile image
Rails Jack

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.