CRA’s build/ is just another artifact directory
Create React App and some older toolchains write production files to build/ instead of dist/. The hosting steps are identical to any other static output: upload the contents, verify HTTPS preview, mind the SPA routing rule. StaticHost does not care what the folder was named on disk. It cares that index.html and the hashed assets are reachable as real files.
StaticHost will not run npm run build for you. There is no forever-free plan after a ~1 day trial. Plans: Starter $9 (1 site, 2 GB), Pro $30 (3 sites, 10 GB, staging), Scale $65 (10×30 GB), Business $130 (30×100 GB, teams). No cPanel, email, built-in CDN product, or PHP. nginx: try_files $uri $uri/ =404—no SPA index fallback.
Produce build/
npm ci
npm run build
ls build
Expect something like:
build/
index.html
static/js/…
static/css/…
asset-manifest.json
package.json homepage setting affects asset prefixes:
{
"homepage": "."
}
or "homepage": "/" for absolute-from-root assets. A leftover "homepage": "https://username.github.io/repo" rewrites asset paths for project pages and breaks root hosting on StaticHost—change it and rebuild.
Env vars use the REACT_APP_ prefix and bake in at build time:
REACT_APP_API_URL=https://api.example.com npm run build
StaticHost will not inject them later. Vite users should see host a Vite build online; this article stays CRA/build/-oriented. Broader React notes: host a React build online.
Pack correctly
cd build
zip -r ../cra-site.zip .
unzip -l ../cra-site.zip | head
If you see build/index.html inside the listing, you zipped from the wrong level—zip a static site without 404. Sibling article for the other common folder name: host a dist folder online.
GitHub: publish a branch whose root equals build/ contents. No remote npm ci—git deploy a static site.
Worked example: homepage fix then stage
Suppose your CRA app was previously on GitHub Pages under /my-app/:
- Open
package.jsonand set"homepage": "."(or"/"). - Search the repo for
basename="/my-app"in React Router and remove it for root hosting. - Rebuild:
rm -rf build
REACT_APP_API_URL=https://api.staging.example.com npm run build
cd build && zip -r ../../cra-staging.zip .
- Deploy to a Pro staging target ($30/mo includes staging) or a second site. Open HTTPS preview. Confirm
/static/js/…returns 200. - Rebuild with production
REACT_APP_API_URL, deploy to production, then attach the custom domain after DNS verifies and the certificate issues.
Routing
CRA apps commonly use React Router BrowserRouter. On StaticHost that means refresh 404s for deep links unless you switch to HashRouter or prerender files. There is no SPA fallback. Mandatory reading: host a single-page application.
serviceWorker legacy note
Older CRA templates registered service workers that cache aggressively. If users are stuck on old UI after deploy, unregister or version carefully. Test on HTTPS (required for SW)—preview HTTPS is enough to exercise this before DNS cutover.
When not to upload build/
- You still have only
src/and never ran production build. - You zipped the repo root including
node_modules“just in case.” - You need
next startor another Node server—wrong host. - You need history-mode deep links without prerender and refuse hash routing.
Verify on preview
- Deploy zip; open HTTPS preview (TLS immediate).
- Confirm
/static/js/…returns 200. - Hard-refresh a client route; interpret 404 correctly.
- Roll back via deploy history if needed.
- Custom domain after DNS verification—static hosting with a custom domain.
Failure table (build/ uploads)
| Symptom | Likely cause | Fix |
|---|---|---|
Blank page; JS 404 under /my-app/static/… | GitHub Pages homepage leftover | Fix homepage, rebuild, redeploy |
Archive extracts to build/index.html path | Zipped parent folder | cd build before zip |
| Staging API called from production | Wrong env at build time | Rebuild with correct REACT_APP_* |
| Deep link 404 | BrowserRouter without files | HashRouter or prerender |
| Users see week-old UI | Legacy service worker | Unregister SW; refresh strategy |
Do not upload node_modules or the CRA source tree as a substitute for build/.
Extra procedure: unregister a sticky legacy service worker
Older CRA templates registered service workers that cache index.html and hashed bundles aggressively. After you upload a corrected build/, a fraction of users still see the broken UI.
- Deploy a build that includes an intentional SW update strategy (or removes registration).
- On HTTPS preview, open Application → Service Workers in DevTools; unregister.
- Hard-reload; confirm Network shows fresh
/static/js/…hashes. - If you keep a SW, version the cache name on every release and call
skipWaitingthoughtfully—test on preview HTTPS before DNS cutover. - Communicate “hard refresh once” only as a temporary bridge; do not rely on users forever.
StaticHost serves the new files immediately; browsers and workers may not. That gap is client behavior, not missing SPA fallback. nginx remains try_files $uri $uri/ =404.
Homepage and basename audit script
Before every production zip:
node -e "const p=require('./package.json'); console.log('homepage=', p.homepage)"
grep -R "basename=" src || true
rm -rf build
REACT_APP_API_URL=https://api.example.com npm run build
grep -R "/my-app/" build | head || echo "no /my-app/ prefix"
cd build && zip -r ../build-prod.zip .
If homepage still points at https://username.github.io/my-app, stop and fix it. Asset paths baked for project pages will 404 on a root StaticHost site even when the zip structure is perfect.
Staging versus production without editing minified JS
Pro ($30/mo, 3 sites, 10 GB, staging) exists so you bake REACT_APP_* twice—once for staging APIs, once for production—not so you sed bundles on the server. Scale ($65) and Business ($130) raise caps; Starter ($9, 1×2 GB) is enough for a single CRA marketing app. Trial ~1 day; no forever-free, email, cPanel, PHP, or built-in CDN product. Companion articles: host a React build online, host a dist folder online, git deploy a static site.
Worked micro-checklist the night before cutover
Print this beside your deploy button:
homepageinpackage.jsonis.or/, never a GitHub Pages project URL.- React Router
basenamematches root hosting (usually no/my-app). REACT_APP_*values are production values for the production zip.unzip -lshowsindex.htmlat the archive root, notbuild/index.html.- HTTPS preview loads;
/static/js/…returns 200; hard-refresh behavior matches your router choice. - Rollback control exercised once this week.
- DNS changes only after step 5 is boringly green—certificate waits on verification.
If any row fails, do not negotiate with the host for SPA fallback or remote env injection. Fix the artifact. StaticHost serves files; CRA’s build/ folder is simply the file tree you promised.
FAQ
Is build/ deprecated because CRA is in maintenance?
Many apps still emit build/. The folder name remains a valid StaticHost artifact. New projects may prefer Vite dist/.
Can I rename build to dist before zipping?
Yes locally; zip contents so the archive root still has index.html.
Why is the page blank after upload?
Usually wrong homepage asset paths or JS errors. Check the console and Network tab.
Does asset-manifest.json need to be public?
It ships in build/ by default; harmless. Do not put secrets there.
How do I stage a CRA build?
Build with staging REACT_APP_* and deploy to a Pro staging target or separate site. Promote by rebuilding with production values—not by editing JS on the server.