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 cigit deploy a static site.

Worked example: homepage fix then stage

Suppose your CRA app was previously on GitHub Pages under /my-app/:

  1. Open package.json and set "homepage": "." (or "/").
  2. Search the repo for basename="/my-app" in React Router and remove it for root hosting.
  3. Rebuild:
rm -rf build
REACT_APP_API_URL=https://api.staging.example.com npm run build
cd build && zip -r ../../cra-staging.zip .
  1. Deploy to a Pro staging target ($30/mo includes staging) or a second site. Open HTTPS preview. Confirm /static/js/… returns 200.
  2. 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 start or another Node server—wrong host.
  • You need history-mode deep links without prerender and refuse hash routing.

Verify on preview

  1. Deploy zip; open HTTPS preview (TLS immediate).
  2. Confirm /static/js/… returns 200.
  3. Hard-refresh a client route; interpret 404 correctly.
  4. Roll back via deploy history if needed.
  5. Custom domain after DNS verification—static hosting with a custom domain.

Failure table (build/ uploads)

SymptomLikely causeFix
Blank page; JS 404 under /my-app/static/…GitHub Pages homepage leftoverFix homepage, rebuild, redeploy
Archive extracts to build/index.html pathZipped parent foldercd build before zip
Staging API called from productionWrong env at build timeRebuild with correct REACT_APP_*
Deep link 404BrowserRouter without filesHashRouter or prerender
Users see week-old UILegacy service workerUnregister 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.

  1. Deploy a build that includes an intentional SW update strategy (or removes registration).
  2. On HTTPS preview, open Application → Service Workers in DevTools; unregister.
  3. Hard-reload; confirm Network shows fresh /static/js/… hashes.
  4. If you keep a SW, version the cache name on every release and call skipWaiting thoughtfully—test on preview HTTPS before DNS cutover.
  5. 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:

  1. homepage in package.json is . or /, never a GitHub Pages project URL.
  2. React Router basename matches root hosting (usually no /my-app).
  3. REACT_APP_* values are production values for the production zip.
  4. unzip -l shows index.html at the archive root, not build/index.html.
  5. HTTPS preview loads; /static/js/… returns 200; hard-refresh behavior matches your router choice.
  6. Rollback control exercised once this week.
  7. 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.