How to Deploy a React App
A React app is a set of files your visitors’ browsers download and run. Deploying one means building those files and serving them fast, over HTTPS, at an address people can remember. This guide takes a React app from your GitHub repository to a live address in about fifteen minutes, then connects your own domain.
On this page
It covers apps built with Vite, which is what most new React projects use, and with Create React App. If you use Next.js, follow How to Deploy a Next.js App instead, because a Next.js app runs a server as well.
Before you start
- A Chajio Cloud App Hosting plan. The Free plan is enough to follow along. Once you order, you get an email with your sign-in details, and the first time you sign in you choose your own password.
- Your code on GitHub, in a repository you own or can access. A public repository you don't own works too.
- An app that builds on your computer with
npm run build, with its lockfile (package-lock.json,yarn.lockorpnpm-lock.yaml) committed to the repository.
Step 1: Get your app ready
If you use Vite
There is nothing to change. Chajio Cloud recognises a Vite project, runs npm run build, and serves the dist folder from a fast static file server. Any path that isn’t a real file gets your index.html, so routes handled by React Router still work when a visitor refreshes the page or opens a link directly.
One thing worth doing is choosing your Node.js version. Without it your app builds on an older default. Add a file called .nvmrc to the root of your repository containing just the major version:
22An engines entry in package.json works too, for example "engines": { "node": "22.x" }.
If you use Create React App
Create React App’s npm start runs the development server, which is slow and not meant for visitors. Tell the platform to serve the production build instead. Add the serve package:
npm install serveThen add a file called Procfile, with no extension, to the root of your repository. Its web: line is the command that starts your app:
web: npx serve -s build -l tcp://0.0.0.0:$PORT-s sends unknown paths to index.html so client-side routes work, and $PORT is the port the platform gives your app. Commit both changes. Create React App is no longer maintained, so for a new project, Vite is the better choice.
Step 2: Create a project
A project is the home for one app, along with its database and domain. In your dashboard, open Projects and click New project:
- Name
- Lowercase letters, numbers and hyphens, up to 24 characters. It becomes part of your app’s free web address and can’t be changed later, so pick something short, like shop or api.
- Plan
- The plan the project draws its resources from.
- Resources
- CPU, memory and storage for this project, pre-filled with what your plan has left. The defaults suit most apps.
Click Create project. Setting it up takes about a minute, and you land on the deploy guide straight away, which walks you through three short steps: Source, Configure and Deploy. You can pick your repository while the project finishes setting up.
Step 3: Connect your repository
On the Source step, choose where your code comes from:
- Your repository
- Click Connect GitHub and approve access, which lets Chajio Cloud read your code and hear about your pushes. Then choose the repository and the branch to deploy, usually main. From now on, every push to that branch deploys automatically.
- Public repository
- Paste the URL of any public GitHub repository, no GitHub sign-in needed. Since you don’t own it, pushes can’t trigger deploys: deploy by hand, or tick Auto-deploy new releases to follow the project’s published releases.
If your app lives in a subfolder of the repository, as in a monorepo, set Build context to that folder, for example apps/web. Otherwise leave it as ., the repository root. Then click Connect repository and Continue.
Step 4: Configure
The Configure step has two runtime settings, both changeable later without a rebuild, and your environment variables.
- Container port
- Leave it at 3000. The static server listens on whatever port you set here, and so does the serve command above.
- Health check path
- Leave it as /. Your home page answers it.
Environment variables in a React app
Your React code runs in the visitor’s browser, so it can’t read variables from the server when it runs. Instead, the build copies them into your JavaScript. Only variables with the right prefix are copied: VITE_ for Vite and REACT_APP_ for Create React App. Add yours here, for example:
VITE_API_URL=https://api.example.com
VITE_SUPABASE_URL=https://your-project.supabase.co
VITE_SUPABASE_PUBLISHABLE_KEY=your-publishable-keyAnd read them in your code:
// Vite
const apiUrl = import.meta.env.VITE_API_URL;
// Create React App
const apiUrl = process.env.REACT_APP_API_URL;
export const getOrders = () => fetch(`${apiUrl}/orders`).then((r) => r.json());Anyone can read these values
Whatever goes into a VITE_ or REACT_APP_ variable ends up in the JavaScript your visitors download. Use them for public values: your API’s address, publishable keys, analytics IDs. Never put a database password, a secret API key or a payment secret in one. Those belong on a server.
Because these values are fixed when your app is built, changing one needs a new build. After you save a change on the Environment tab, the dashboard marks the variables your build used with Build and offers a Redeploy button. A plain restart would keep serving the old value.
Click Save and continue.
Step 5: Deploy
The Deploy step shows a summary of the repository, branch and build context. Click Deploy now and the build log starts streaming: your code is fetched, dependencies are installed and your app is built and started. The deployment moves through Queued, Building and Deploying to Success.
The first build takes a few minutes. Later builds reuse the parts that didn't change, so they are usually quicker. When it finishes, your app is live on a free live.chajio.cloud address with HTTPS already switched on. You'll find the address on the project's Overview.
If the deployment fails, the dashboard names the stage that broke and why, and the build log shows the details. Our troubleshooting guide covers the usual causes.
Talking to your backend
If your React app calls an API you also host on Chajio Cloud, deploy the API as its own project, following the Node.js guide or the Python guide. Then set VITE_API_URL to its https address. Browsers only let a page call an API on another domain if the API allows it, so add your React app’s address to the API’s CORS settings, for example https://www.example.com.
Using your own Dockerfile
If your repository has a file named Dockerfile in its root (or in the build context folder), Chajio Cloud builds from it instead of detecting your setup. That suits React frameworks that render on a server, such as React Router’s framework mode or TanStack Start, and anything with an unusual build. Two rules matter:
- Declare each build-time variable with an
ARGline before the build command. EveryARGreceives the variable with the same name from your Environment tab. Variables you don’t declare never reach the build. - Listen on
$PORT, on all network interfaces (0.0.0.0), not only onlocalhost.
FROM node:22-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
# Each ARG receives the Environment variable with the same name.
ARG VITE_API_URL
ARG VITE_SUPABASE_URL
ARG VITE_SUPABASE_PUBLISHABLE_KEY
RUN npm run build
FROM node:22-alpine
WORKDIR /app
RUN npm install -g serve@14
COPY --from=build /app/dist ./dist
USER node
CMD ["sh", "-c", "serve -s dist -l tcp://0.0.0.0:${PORT:-3000}"]An ARG is available to the RUN commands after it, so npm run build sees the value without an extra ENV line. Our Dockerfile guide covers the rest.
Connect your domain
Your app already works on its free address. To serve it from your own domain, open the project's Domains tab:
- Enter your domain, for example
www.example.com, and click Add domain. - The dashboard shows two DNS records to add where your domain's DNS is managed. A TXT record proves the domain is yours. A CNAME record routes visitors to your app, or an A or ALIAS record for a root domain like
example.com. Copy the values exactly as shown. - Click Verify. Once both records check out, a free HTTPS certificate is issued automatically, usually in under a minute, and your app is live on your domain.
DNS changes can take a while to spread, so if Verify doesn't find a record yet, give it a few minutes; the dashboard also re-checks on its own. The full walkthrough, including root domains and Cloudflare, is in How to connect your domain to your app.
After launch
- Every push deploys. Push to your branch and a new deployment starts on its own. Your current version keeps serving visitors until the new one passes its health check, so updates don't cause downtime. You can also deploy by hand from the Deployments tab.
- Roll back in seconds. If a release misbehaves, open Deployments and click Rollback to this version on an earlier successful deployment. Nothing is rebuilt; the previous version simply comes back.
- Watch it run. The Logs tab streams your app's output live, with secrets hidden. The Overview shows your app's status and address, with Redeploy, Restart and Stop buttons.
Your app runs as two copies
Every plan runs two copies of your app, so a crash or an update never takes it offline. That has one consequence worth designing for: anything an app keeps only in its own memory or on its own disk isn't shared with the other copy, and it's gone after a redeploy.
A React app keeps its state in the visitor’s browser, so this rarely affects it. It matters for the API behind it: keep anything you need to keep in a database, not in the server’s memory or on its disk.
Common problems
A page works from the home page but shows “not found” after a refresh
The server doesn’t know your client-side routes. Vite projects are handled for you. With Create React App, make sure your start command uses serve -s, as in the Procfile above.
A variable is undefined in the browser
Check the prefix (VITE_ or REACT_APP_), then redeploy. Build-time values only change with a new build, so a restart doesn’t pick them up.
The page is blank and the browser console shows 404s for JavaScript files
Your build expects to live in a subfolder. Remove any base setting from vite.config, or the homepage field from package.json for Create React App, left over from hosting on a path like GitHub Pages.
The build fails with “npm ci can only install packages when your package.json and package-lock.json are in sync”
Run npm install on your computer, commit the updated lockfile and push again.
Create React App fails with “Treating warnings as errors”
Builds run in CI mode, where Create React App refuses to finish with lint warnings. Fix the warnings the log lists, which is the better fix, or change your build script to "build": "CI=false react-scripts build".
For anything else, Deployment Failed? How to Find and Fix the Cause walks through reading a failed deployment.
Learning path
Deploy your first app
- 1
Deploy your app
- 2
- 3
- 4
Keep reading
How to Connect Your Domain to Your App (with Free HTTPS)
Put your app on your own domain in three DNS steps. Covers www versus root domains, what each DNS record does, Cloudflare, and what to check when a domain will not verify.
How to Deploy a Next.js App
Deploy a full Next.js app, with server rendering, API routes and middleware, straight from GitHub. Covers public and server-side variables, databases and connecting your domain.
How to Deploy Any App with a Dockerfile
Go, PHP, .NET, Rust or anything else: if it runs from a Dockerfile, it runs on Chajio Cloud. The rules your Dockerfile needs to follow, build-time variables, and a worked Go example.
Deployment Failed? How to Find and Fix the Cause
Every failed deployment tells you which stage broke and why. How to read it, and the fixes for the failures we see most: missing start commands, port mix-ups, health checks and memory.
Ready to deploy?
Start on the Free plan: your first month is on us, and every app runs two copies for high availability.