Mastering React Deployment on GitHub Pages: Professional Practices
React applications often get hosted on cloud providers, but for individual portfolios or lightweight demos, GitHub Pages remains highly effective—despite skepticism about its suitability for complex SPAs. With correct configuration, you’ll sidestep pathing issues, enjoy zero operational cost, and reduce deployment friction to a single command.
Why Bother with GitHub Pages for React?
Strength | Detail |
---|---|
Cost | Free static hosting, no bandwidth charges |
Availability | Served via GitHub CDN with auto HTTPS |
Custom Domains | Supported out of the box, including DNS and LetsEncrypt |
Public Visibility | Ideal for live demos and CVs; no infra to maintain |
Side note: For apps requiring server-side API integrations, the lack of backend limits applicability. For pure frontend portfolios, it remains unbeaten in simplicity.
1. Prepare the React App
Generate a new React app with Create React App (CRA) 5.x or later:
npx create-react-app@5.0.1 my-react-app
cd my-react-app
Production readiness check: Remove unfinished code, commit only the final static assets.
2. Specify Deployment Subpath
Without correctly setting "homepage"
, asset paths break, especially if your repo name differs from your app name or when using organization accounts.
In package.json
:
"homepage": "https://<github-username>.github.io/<repository-name>"
If deploying to a custom domain, replace with "https://yourdomain.com"
.
3. Automate Builds & Deployment
Install the gh-pages
package:
npm install --save-dev gh-pages@4.0.0
In package.json
, configure scripts precisely. Overriding pre-existing deploy scripts is common after experimentation:
"scripts": {
"predeploy": "npm run build",
"deploy": "gh-pages -d build",
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test"
}
predeploy
: Ensures a fresh production bundle.deploy
: Pushes build output to thegh-pages
branch.
4. GitHub Integration
Initialize git, commit the app, push to remote.
git init
git remote add origin https://github.com/<username>/<repository>.git
git add .
git commit -m "Initial commit"
git branch -M main
git push -u origin main
Replace remote if already set, see git remote -v
.
5. Run Deployment
The sequence:
npm run deploy
- Bundles to
/build
- Publishes to the
gh-pages
branch - Sets up the static site for GitHub Pages
Check the GitHub Actions log: if the action is blocked (e.g., “gh-pages branch is protected”), adjust branch protection rules or permissions.
6. GitHub Pages Configuration
Repository → Settings → Pages:
- Set source branch:
gh-pages
, directory:/ (root)
- Click “Save”
- Confirm published URL. Usually matches the
homepage
value.
Note: It can take a few minutes for DNS/CDN propagation.
Routing, Broken Links, and Rendering Issues
A typical React gotcha: refreshing on a subpath (e.g., /about
) leads to a 404. GitHub Pages serves only static files, no rewrite to index.html
happens.
Mitigations:
-
Use
HashRouter
in place ofBrowserRouter
:import { HashRouter } from 'react-router-dom'; export default function App() { return ( <HashRouter> {/* ...routes */} </HashRouter> ); }
Downside: URLs include the
#
symbol (example:/#/about
). -
Advanced: Place a custom
404.html
in/public
that redirects all unmatched requests to/index.html
. See rafgraph/spa-github-pages for a reference implementation. Caveat: Subtle issues for apps with unusual routing bases.
Known Issue: Sometimes the first deployment stalls with “Page not found”. Check CNAME clashes, repository visibility (public), and clear browser cache if URL keeps returning old content.
Practical Example: package.json
Excerpt
{
"name": "my-react-app",
"version": "0.1.0",
"private": true,
"homepage": "https://johndoe.github.io/my-react-app",
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.12.1"
},
"devDependencies": {
"gh-pages": "^4.0.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"predeploy": "npm run build",
"deploy": "gh-pages -d build"
}
}
Non-obvious Tips
- Lock
gh-pages
version: New releases sometimes change default behavior; builds may fail silently. - If you need environment variables (
REACT_APP_*
), ensure they’re valid at build time. Secrets will not be available at runtime on GitHub Pages. - For CI/CD: Add a GitHub Actions workflow to automate pushing to
gh-pages
onmain
update—removes need for local deploys.
Documents like this can make the difference between a clean, live portfolio and “project not found”. GitHub Pages isn’t for everyone, but for static React apps, with mindful routing setup and correct asset pathing, it’s robust and nearly effortless.
Got a more complicated multi-branch scenario? Consider pushing multiple apps to different subdirectories using a mono-repo and customizing gh-pages -d build/subdir
. But that’s another layer—keep it simple unless you need that flexibility.