Dockerize Production React.js
Tags: docker, react, nginx
Published: 2022-10-07 19:15:49

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
- [Docker](https://docs.docker.com/get-docker/)
- [Node.js](https://nodejs.org/en/)

First, ensure Docker is installed on your machine. If you haven't please install it and come here again.

```bash
docker -v
```

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

```bash
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](https://vitejs.dev/) because it's fast and configurable.

> **HINT**
>
> You may use [Create React App (CRA)](https://create-react-app.dev/) to intialize your application. It's also good and developed by Facebook Open Source too.

Open your terminal and type the following command.

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

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

```bash
npm run dev
```

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

![Vite + React](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pa28cp8afmlpsd1nbm2c.png)

## 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.

```Docker
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.

```Dockerfile
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`.

```bash
mkdir -p docker/nginx/conf.d; touch docker/nginx/conf.d/default.conf
```

...and follow below snippet.

```conf
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`...

```Docker
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.

```bash
touch .dockerignore
```
Put it inside `.dockerignore`

```text
.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.

```bash
docker build -t web:0.1.0 .
```

Ensure the image is available.

```bash
docker image ls
```

## Testing

Let's verify our works.

```bash
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.

![Vite + React](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gxkusffawcy085364lfs.png)

Yeay! It's working...

## Bonus: The Source Code

Find it [here](https://github.com/thexdev/devlabs/tree/main/docker-production-react).

Also follow my latest experiment.

{% embed https://github.com/thexdev/devlabs %}