This tutorial shows two Docker setups for a React 19 app:
- Development container (fast iteration + hot reload)
- Production image (small, optimized static build served by Nginx)
It builds on the Docker fundamentals (images, containers, Dockerfile, docker-compose, .env) you covered in your slides. fileciteturn0file0
- Docker Desktop installed and running
- Node.js (recommended v20+) for local scaffolding
- A terminal
Verify Docker:
docker --version
docker compose versionIn an empty folder:
npm create vite@latest react19-docker -- --template react
cd react19-docker
npm install
npm run devConfirm the app works at the URL printed by Vite (usually http://localhost:5173).
Create a file named .dockerignore:
node_modules
dist
.git
.gitignore
Dockerfile
docker-compose.yml
npm-debug.log
yarn-error.log
.DS_Store
Create Dockerfile.dev in the project root:
# Dev image for React (Vite) with hot reload
FROM node:20-alpine
WORKDIR /app
# Install deps first (better caching)
COPY package*.json ./
RUN npm ci
# Copy project
COPY . .
# Vite dev server port
EXPOSE 5173
# Important: Vite needs --host so it is reachable from outside the container
CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0", "--port", "5173"]Create docker-compose.yml:
services:
web:
build:
context: .
dockerfile: Dockerfile.dev
ports:
- "5173:5173"
volumes:
# Mount your source for live edits
- .:/app
# Keep container's node_modules (avoid overwriting by the host)
- /app/node_modules
environment:
- CHOKIDAR_USEPOLLING=truedocker compose up --buildOpen:
http://localhost:5173
Edit a component (e.g., src/App.jsx) and you should see hot reload.
Stop containers:
docker compose downFor production we’ll build the static site and serve it efficiently.
Create Dockerfile:
# ---- Build stage ----
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# ---- Run stage ----
FROM nginx:alpine
# Copy build output to Nginx web root
COPY --from=build /app/dist /usr/share/nginx/html
# Optional: custom Nginx config to support SPA routing
# (uncomment if you add the config file in step 4.2)
# COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]If you use React Router (client-side routes like /about), create nginx.conf:
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
}Then uncomment the COPY nginx.conf ... line in the Dockerfile.
Build:
docker build -t react19-prod .Run:
docker run --rm -p 8080:80 react19-prodOpen:
http://localhost:8080
For Vite, environment variables exposed to the browser must start with VITE_.
Create .env:
VITE_API_BASE_URL=https://example.com/apiUse it in code:
const baseUrl = import.meta.env.VITE_API_BASE_URL;Important: don’t commit .env to Git. Add it to .gitignore.
- Ensure your dev command includes
--host 0.0.0.0 - Try polling (already included in compose):
CHOKIDAR_USEPOLLING=true
- Change host mapping, e.g.
5174:5173 - Or stop the process using the port
- Make sure you kept
/app/node_modulesas an anonymous volume in compose:- /app/node_modules
- Add the
nginx.confwithtry_files ... /index.html(step 4.2)
Dev
docker compose up --build
docker compose downProd
docker build -t react19-prod .
docker run --rm -p 8080:80 react19-prod