Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Building and deploying

The npm run dev server is not for real visitors: it is slower and rebuilds code on the fly. Before publishing, the project is built into optimized files, which are then run by an ordinary Node.js server. Deployment means moving the built app to a server or hosting platform.

Building

npm run build

The script runs react-router build. A build/ folder appears:

build/
├── client/           browser files: JS, CSS, images, prerendered HTML
└── server/
    └── index.js      the server part: loaders, actions, rendering

Running

npm start

The script runs react-router-serve ./build/server/index.js. It is a ready-made server from the @react-router/serve package: it serves files from build/client and passes other requests to the app. The site opens at http://localhost:3000; set the PORT environment variable for a different port.

On your own server

The server needs Node.js 20+ and the environment variables from the lesson about them. The steps:

npm ci
npm run build
SESSION_SECRET=... NODE_ENV=production npm start

The process is usually kept running with systemd or pm2, with Nginx and HTTPS in front. Remember that the blog.db database file must live where the next deployment will not wipe it.

Docker

The project template already contains a Dockerfile that builds and runs the app:

docker build -t blog .
docker run -p 3000:3000 -e SESSION_SECRET=long-secret blog

The site is available at http://localhost:3000. The image runs on any host that supports containers.

Adapters

react-router-serve fits simple cases. When the app has to live inside your own server or run on a special platform, you use an adapter — a package that connects the framework to that environment:

PackagePurpose
@react-router/nodeThe base for Node.js, other Node adapters build on it
@react-router/expressThe app inside your own Express server
@react-router/cloudflareCloudflare Workers and Pages
@react-router/architectAWS Lambda via Architect

For example, with Express the app is plugged in as an ordinary handler:

// server.js
import express from "express";
import { createRequestHandler } from "@react-router/express";
 
const app = express();
app.use(express.static("build/client"));
app.use(createRequestHandler({ build: await import("./build/server/index.js") }));
app.listen(3000);

Vercel and Netlify have their own ways of connecting; create-react-router has ready templates for different platforms (the --template option), listed in the React Router documentation.

Common mistake

Uploading only the build/ folder to the server and running npm start. The built code still imports packages from node_modules — react, react-router, the database driver. Copy package.json and the lock file to the server too and run npm ci --omit=dev to install the production dependencies.

Thanks for reading!

Donate to the site — we work hard for you.