A static build has no server behind it, so whatever is wrong in these files is wrong for every visitor. React2Static runs all of the following against your own build output before you ship. Here is the whole list, with the fix for each.
The package names, paths and sizes in the examples below are only examples — the builder fills in the ones from your own project.
What you see
dist/
.env
index.html
assets/index-a3f1c2.jsThe whole file is downloadable by anyone who visits. Rotate every value in it, then keep .env out of the folder your build copies verbatim — usually public/.
What you see
const apiKey = process.env.API_KEY;
fetch(url, { headers: { Authorization: `Bearer ${apiKey}` } });
// Vite turns process.env into {} — the header goes out as
// "Bearer undefined" and the API answers 401. No error in the console.Vite replaces process.env with an empty object, so this value is simply undefined in the browser — no error, no warning, the request just goes out without it and the API answers 401. Read it through import.meta.env.VITE_something instead, or define it in vite.config.
The fix
const apiKey = import.meta.env.VITE_API_KEY;What you see
Uncaught ReferenceError: process is not defined
at index-a3f1c2.js:1:2043Browsers have no process object, so this line throws the moment the page loads — usually a blank screen. In Vite, values the browser needs must be read through import.meta.env and named VITE_something.
The fix
const apiKey = import.meta.env.VITE_API_KEY;What you see
const API = "http://localhost:3000/api";It contains http://localhost:3000/api. That address only exists on the computer this was built on, so the request fails for every visitor. Point it at a public URL.
The fix
const API = "https://api.example.com";What you see
dist/
assets/index-a3f1c2.js
assets/index-9b2e01.cssA static host serves index.html as the entry page. Without one, visitors get a file listing or a 404. Check what your build actually writes.
What you see
<title>Vite + React + TS</title>It currently reads “Vite + React + TS”. That is the text search results and shared links will show. Change it in index.html.
The fix
<title>Your product name — what it does, in one line</title>The browser tab and every search result use the <title> tag. Add one in index.html.
The fix
<title>Your product name — what it does, in one line</title>Search engines fall back to whatever fragment they can scrape. One clear sentence in <meta name="description"> is what decides whether people click.
The fix
<meta name="description" content="One clear sentence about what this page is for." />Phones will render the page at desktop width and zoom out, so everything looks tiny. Add <meta name="viewport" content="width=device-width, initial-scale=1"> to index.html.
The fix
<meta name="viewport" content="width=device-width, initial-scale=1" />Shared to a chat app or a social feed, your link shows as a bare URL with no title or image. Add og:title, og:description and og:image.
The fix
<meta property="og:title" content="Your product name" />
<meta property="og:description" content="One clear sentence." />
<meta property="og:image" content="https://example.com/og.png" />What you see
<link rel="icon" type="image/svg+xml" href="/vite.svg" />The browser tab and bookmarks show a blank sheet, or the template logo you inherited. Add your own favicon and link it from index.html.
The fix
<link rel="icon" href="/favicon.ico" sizes="any" />Screen readers use it to pick a pronunciation and browsers use it to offer translation. Add lang="en", or whichever language the page is in.
The fix
<html lang="en">Crawlers will index the site anyway, but robots.txt is where you point them at your sitemap. We can add one at the Ship step.
The fix
User-agent: *
Allow: /
Sitemap: https://example.com/sitemap.xmlWhat you see
dist/assets/index-a3f1c2.js 2,418.55 kB │ gzip: 731.20 kB
(!) Some chunks are larger than 500 kB after minification.On a phone connection that is seconds of blank screen before anything appears. Splitting the heavy parts out with dynamic import() usually cuts the first load a long way.
The fix
const Editor = lazy(() => import('./Editor'));What you see
dist/assets/hero-8f2a1b.png 4,102.11 kBLarge images and fonts hold up the first paint. Converting images to WebP and resizing them to the size actually displayed usually removes most of the weight.
Drop your ZIP into the builder. It runs the real build in your browser, and when something breaks it points at the line that matters instead of handing you the whole log.
Open the builder