Q&A
How to deploy a React app to Heroku
Overview
A React app created with tools like Create React App or Vite is, in production, just static files (HTML/CSS/JS) produced by a build step. Deploying to Heroku means getting Heroku to (1) run that build and (2) serve the resulting files. Because Heroku runs a process per app, the common approach is to serve the build with a minimal server.
graph LR
A[git push heroku main] --> B[Heroku detects Node app]
B --> C[Runs npm install + build script]
C --> D[Runs start script: serve build/]
D --> E[App live at *.herokuapp.com]
style A fill:#1e1b4b,color:#fff
Prerequisites
- A React app in a git repository.
- The Heroku CLI installed and
heroku logindone. - A Heroku account.
Approach 1: Serve with the serve package (simplest)
Step 1 - add serve and scripts. In package.json:
{
"scripts": {
"start": "serve -s build",
"build": "react-scripts build",
"heroku-postbuild": "npm run build"
},
"dependencies": {
"serve": "^14.0.0"
}
}
heroku-postbuildruns automatically after install, producing thebuild/folder.startserves that folder;-senables single-page-app routing (all routes →index.html).
Step 2 - create and deploy:
heroku create my-react-app
git add .
git commit -m "Prepare for Heroku"
git push heroku main # triggers build + start
heroku open # open the live URL
Approach 2: Express server (more control / API in same app)
Create a small server that serves the build and can also host API routes:
// server.js
const express = require("express");
const path = require("path");
const app = express();
app.use(express.static(path.join(__dirname, "build")));
// SPA fallback: send index.html for any non-file route
app.get("*", (req, res) => {
res.sendFile(path.join(__dirname, "build", "index.html"));
});
const port = process.env.PORT || 3000; // Heroku injects PORT
app.listen(port, () => console.log(`Listening on ${port}`));
"scripts": {
"start": "node server.js",
"build": "react-scripts build",
"heroku-postbuild": "npm run build"
}
Critical: bind to
process.env.PORT. Heroku assigns the port dynamically; hard-coding a port causes the app to crash on boot.
Approach 3: Static buildpack
For a pure static SPA you can use a static buildpack (e.g. heroku-buildpack-static or a community buildpack) with a static.json describing routing. This serves files via nginx without a Node process - lighter, but less flexible than an Express server.
Environment Variables
React build-time variables (Create React App: prefixed REACT_APP_) must be set as Heroku config vars so they're present during the build:
heroku config:set REACT_APP_API_URL=https://api.example.com
Verifying and Debugging
heroku logs --tail # watch live logs
heroku ps # see running dynos
Common boot failures show up here - usually a missing start script, not binding to $PORT, or a build error.
Why This Matters in Interviews
This ties together build tooling, the PaaS model (Heroku from this batch's Cloud Computing question), and production serving of an SPA. Key points: a React app builds to static files; Heroku needs a start process; you must bind to process.env.PORT; and SPA routing needs an index.html fallback. Knowing heroku-postbuild and config vars shows real deployment experience.
Common Pitfalls
- Hard-coding the port instead of
process.env.PORT→ app crashes. - Forgetting the SPA fallback, so client-side routes 404 on refresh.
- Build-time env vars not set as config vars before the build runs.
- Committing
node_modulesorbuild/instead of letting Heroku build. - Wrong branch name (
git push heroku mainvsmaster).
References
- Heroku Node.js deployment: https://devcenter.heroku.com/articles/deploying-nodejs
- Create React App deployment guide: https://create-react-app.dev/docs/deployment/