docs/content/guides/reactjs/containerize.md

16 KiB
Raw Permalink Blame History

title linkTitle weight keywords description
Containerize a React.js Application Containerize 10 react.js, node, image, initialize, build Learn how to containerize a React.js application with Docker by creating an optimized, production-ready image using best practices for performance, security, and scalability.

Prerequisites

Before you begin, make sure the following tools are installed and available on your system:

  • You have installed the latest version of Docker Desktop.
  • You have a git client. The examples in this section use a command-line based git client, but you can use any client.

New to Docker?
Start with the Docker basics guide to get familiar with key concepts like images, containers, and Dockerfiles.


Overview

This guide walks you through the complete process of containerizing a React.js application with Docker. Youll learn how to create a production-ready Docker image using best practices that improve performance, security, scalability, and deployment efficiency.

By the end of this guide, you will:

  • Containerize a React.js application using Docker.
  • Create and optimize a Dockerfile for production builds.
  • Use multi-stage builds to minimize image size.
  • Serve the application efficiently with a custom NGINX configuration.
  • Follow best practices for building secure and maintainable Docker images.

Get the sample application

Clone the sample application to use with this guide. Open a terminal, change directory to a directory that you want to work in, and run the following command to clone the git repository:

$ git clone https://github.com/kristiyan-velkov/docker-reactjs-sample

Generate a Dockerfile

Docker provides an interactive CLI tool called docker init that helps scaffold the necessary configuration files for containerizing your application. This includes generating a Dockerfile, .dockerignore, compose.yaml, and README.Docker.md.

To begin, navigate to the root of your project directory:

$ cd docker-reactjs-sample

Then run the following command:

$ docker init

Youll see output similar to:

Welcome to the Docker Init CLI!

This utility will walk you through creating the following files with sensible defaults for your project:
  - .dockerignore
  - Dockerfile
  - compose.yaml
  - README.Docker.md

Let's get started!

The CLI will prompt you with a few questions about your app setup. For consistency, please use the same responses shown in the example below when prompted:

Question Answer
What application platform does your project use? Node
What version of Node do you want to use? 22.14.0-alpine
Which package manager do you want to use? npm
Do you want to run "npm run build" before starting server? yes
What directory is your build output to? dist
What command do you want to use to start the app? npm run dev
What port does your server listen on? 8080

After completion, your project directory will contain the following new files:

├── docker-reactjs-sample/
│ ├── Dockerfile
│ ├── .dockerignore
│ ├── compose.yaml
│ └── README.Docker.md

Build the Docker image

The default Dockerfile generated by docker init serves as a solid starting point for general Node.js applications. However, React.js is a front-end library that compiles into static assets, so we need to tailor the Dockerfile to optimize for how React applications are built and served in a production environment.

Step 1: Review the generated files

In this step, youll improve the Dockerfile and configuration files by following best practices:

  • Use multi-stage builds to keep the final image clean and small
  • Serve the app using NGINX, a fast and secure web server
  • Improve performance and security by only including whats needed

These updates help ensure your app is easy to deploy, fast to load, and production-ready.

[!NOTE] A Dockerfile is a plain text file that contains step-by-step instructions to build a Docker image. It automates packaging your application along with its dependencies and runtime environment.
For full details, see the Dockerfile reference.

Step 2: Configure the Dockerfile file

Copy and replace the contents of your existing Dockerfile with the configuration below:

# =========================================
# Stage 1: Build the React.js Application
# =========================================
ARG NODE_VERSION=22.14.0-alpine
ARG NGINX_VERSION=alpine3.21

# Use a lightweight Node.js image for building (customizable via ARG)
FROM node:${NODE_VERSION} AS builder

# Set the working directory inside the container
WORKDIR /app

# Copy package-related files first to leverage Docker's caching mechanism
COPY --link package.json package-lock.json ./

# Install project dependencies using npm ci (ensures a clean, reproducible install)
RUN --mount=type=cache,target=/root/.npm npm ci

# Copy the rest of the application source code into the container
COPY --link . .

# Build the React.js application (outputs to /app/dist)
RUN npm run build

# =========================================
# Stage 2: Prepare Nginx to Serve Static Files
# =========================================

FROM nginxinc/nginx-unprivileged:${NGINX_VERSION} AS runner

# Use a built-in non-root user for security best practices
USER nginx

# Copy custom Nginx config
COPY --link nginx.conf /etc/nginx/nginx.conf

# Copy the static build output from the build stage to Nginx's default HTML serving directory
COPY --link --from=builder /app/dist /usr/share/nginx/html

# Expose port 8080 to allow HTTP traffic
# Note: The default NGINX container now listens on port 8080 instead of 80 
EXPOSE 8080

# Start Nginx directly with custom config
ENTRYPOINT ["nginx", "-c", "/etc/nginx/nginx.conf"]
CMD ["-g", "daemon off;"]

Step 3: Configure the .dockerignore file

The .dockerignore file tells Docker which files and folders to exclude when building the image.

[!NOTE] This helps:

  • Reduce image size
  • Speed up the build process
  • Prevent sensitive or unnecessary files (like .env, .git, or node_modules) from being added to the final image.

To learn more, visit the .dockerignore reference.

Copy and replace the contents of your existing .dockerignore with the configuration below:

# Ignore dependencies and build output
node_modules/
dist/
out/
.tmp/
.cache/

# Ignore Vite, Webpack, and React-specific build artifacts
.vite/
.vitepress/
.eslintcache
.npm/
coverage/
jest/
cypress/
cypress/screenshots/
cypress/videos/
reports/

# Ignore environment and config files (sensitive data)
*.env*
*.log

# Ignore TypeScript build artifacts (if using TypeScript)
*.tsbuildinfo

# Ignore lockfiles (optional if using Docker for package installation)
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*

# Ignore local development files
.git/
.gitignore
.vscode/
.idea/
*.swp
.DS_Store
Thumbs.db

# Ignore Docker-related files (to avoid copying unnecessary configs)
Dockerfile
.dockerignore
docker-compose.yml
docker-compose.override.yml

# Ignore build-specific cache files
*.lock

Step 4: Create the nginx.conf file

To serve your React.js application efficiently inside the container, youll configure NGINX with a custom setup. This configuration is optimized for performance, browser caching, gzip compression, and support for client-side routing.

Create a file named nginx.conf in the root of your project directory, and add the following content:

[!NOTE] To learn more about configuring NGINX, see the official NGINX documentation.

worker_processes auto;

# Store PID in /tmp (always writable)
pid /tmp/nginx.pid;

events {
    worker_connections 1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    # Disable logging to avoid permission issues
    access_log off;
    error_log  /dev/stderr warn;

    # Optimize static file serving
    sendfile        on;
    tcp_nopush      on;
    tcp_nodelay     on;
    keepalive_timeout  65;
    keepalive_requests 1000;

    # Gzip compression for optimized delivery
    gzip on;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml;
    gzip_min_length 256;
    gzip_vary on;

    server {
        listen       8080;
        server_name  localhost;

        # Root directory where React.js build files are placed
        root /usr/share/nginx/html;
        index index.html;

        # Serve React.js static files with proper caching
        location / {
            try_files $uri /index.html;
        }

        # Serve static assets with long cache expiration
        location ~* \.(?:ico|css|js|gif|jpe?g|png|woff2?|eot|ttf|svg|map)$ {
            expires 1y;
            access_log off;
            add_header Cache-Control "public, immutable";
        }

        # Handle React.js client-side routing
        location /static/ {
            expires 1y;
            add_header Cache-Control "public, immutable";
        }
    }
}

Step 5: Build the React.js application image

With your custom configuration in place, you're now ready to build the Docker image for your React.js application.

The updated setup includes:

  • Optimized browser caching and gzip compression
  • Secure, non-root logging to avoid permission issues
  • Support for React client-side routing by redirecting unmatched routes to index.html

After completing the previous steps, your project directory should now contain the following files:

├── docker-reactjs-sample/
│ ├── Dockerfile
│ ├── .dockerignore
│ ├── compose.yaml
│ ├── nginx.conf
│ └── README.Docker.md

Now that your Dockerfile is configured, you can build the Docker image for your React.js application.

[!NOTE] The docker build command packages your application into an image using the instructions in the Dockerfile. It includes all necessary files from the current directory (called the build context).

Run the following command from the root of your project:

$ docker build --tag docker-reactjs-sample .

What this command does:

  • Uses the Dockerfile in the current directory (.)
  • Packages the application and its dependencies into a Docker image
  • Tags the image as docker-reactjs-sample so you can reference it later

Step 6: View local images

After building your Docker image, you can check which images are available on your local machine using either the Docker CLI or Docker Desktop. Since you're already working in the terminal, let's use the Docker CLI.

To list all locally available Docker images, run the following command:

$ docker images

Example Output:

REPOSITORY                TAG               IMAGE ID       CREATED         SIZE
docker-reactjs-sample     latest            f39b47a97156   14 seconds ago   75.8MB

This output provides key details about your images:

  • Repository The name assigned to the image.
  • Tag A version label that helps identify different builds (e.g., latest).
  • Image ID A unique identifier for the image.
  • Created The timestamp indicating when the image was built.
  • Size The total disk space used by the image.

If the build was successful, you should see docker-reactjs-sample image listed.


Run the containerized application

In the previous step, you created a Dockerfile for your React.js application and built a Docker image using the docker build command. Now its time to run that image in a container and verify that your application works as expected.

Inside the docker-reactjs-sample directory, run the following command in a terminal.

$ docker compose up --build

Open a browser and view the application at http://localhost:8080. You should see a simple React.js web application.

Press ctrl+c in the terminal to stop your application.

Run the application in the background

You can run the application detached from the terminal by adding the -d option. Inside the docker-reactjs-sample directory, run the following command in a terminal.

$ docker compose up --build -d

Open a browser and view the application at http://localhost:8080. You should see a simple web application preview.

To confirm that the container is running, use docker ps command:

$ docker ps

This will list all active containers along with their ports, names, and status. Look for a container exposing port 8080.

Example Output:

CONTAINER ID   IMAGE                          COMMAND                  CREATED             STATUS             PORTS                    NAMES
88bced6ade95   docker-reactjs-sample-server   "nginx -c /etc/nginx…"   About a minute ago  Up About a minute  0.0.0.0:8080->8080/tcp   docker-reactjs-sample-server-1

To stop the application, run:

$ docker compose down

[!NOTE] For more information about Compose commands, see the Compose CLI reference.


Summary

In this guide, you learned how to containerize, build, and run a React.js application using Docker. By following best practices, you created a secure, optimized, and production-ready setup.

What you accomplished:

  • Initialized your project using docker init to scaffold essential Docker configuration files.
  • Replaced the default Dockerfile with a multi-stage build that compiles the React.js application and serves the static files using Nginx.
  • Replaced the default .dockerignore file to exclude unnecessary files and keep the image clean and efficient.
  • Built your Docker image using docker build.
  • Ran the container using docker compose up, both in the foreground and in detached mode.
  • Verified that the app was running by visiting http://localhost:8080.
  • Learned how to stop the containerized application using docker compose down.

You now have a fully containerized React.js application, running in a Docker container, and ready for deployment across any environment with confidence and consistency.


Explore official references and best practices to sharpen your Docker workflow:


Next steps

With your React.js application now containerized, you're ready to move on to the next step.

In the next section, you'll learn how to develop your application using Docker containers, enabling a consistent, isolated, and reproducible development environment across any machine.