Learning objectives
After reading this article you will be able to:
- Generate a production build of a React app with Vite
- Make React Router routes survive a page reload
- Set environment variables and point a custom domain at the app
On this page
- What do you need before you start?
- How do you generate the production build?
- Why do routes return 404 when you reload the page?
- How do you use environment variables in React?
- How do you publish the dist folder?
- How do you add a custom domain?
- What are the common mistakes?
- How to do this on Square Cloud
- FAQs
A React app built with Vite does not run on a server. It becomes a folder of files (an index.html, JavaScript, CSS and images) and the browser runs React. Hosting it means publishing that folder somewhere that serves it over HTTPS. You can do it in a few minutes, but two details trip up many people: routes that return 404 on reload, and environment variables that "disappear" or leak.
The examples use Vite, one of the build tools the React documentation points to for people assembling their own project. Create React App was deprecated for new apps in February 2025.
What do you need before you start?#
- A recent Node.js version that meets the minimum listed in the Vite documentation.
- A React project with Vite. To create one from scratch:
npm create vite@latest my-app -- --template react
cd my-app
npm install
npm run devThe -- before --template is required on npm 7 or later. Use --template react-ts for TypeScript. npm run dev starts the development server, which is for coding only and does not go to production.
How do you generate the production build?#
npm run build
npm run previewbuild runs vite build and writes everything to dist/, minified. preview serves dist/ at http://localhost:4173 so you can check the result. The Vite docs warn that preview is not a production server.
The dist/ folder is what you publish. Your source code, node_modules and .env files stay out. If the site will live under a subpath (example.com/app/), set base: '/app/' in vite.config.js. On a custom domain or subdomain, the default '/' is correct.
Why do routes return 404 when you reload the page?#
With React Router in browser mode, the URL /about only exists inside JavaScript:
import { createBrowserRouter, RouterProvider } from "react-router";
const router = createBrowserRouter([
{ path: "/", element: <Home /> },
{ path: "/about", element: <About /> },
]);
export default function App() {
return <RouterProvider router={router} />;
}Clicking a link works because the browser doesn't request anything from the server. When you reload /about, though, the browser requests that path from the server, which looks for a file named about, finds none and answers 404. The server must return index.html for every path that is not a file, and let React Router pick the screen.
Each server does this differently. On Firebase Hosting, for example, it is a rewrites rule in firebase.json pointing ** to /index.html. On Nginx, it is try_files $uri /index.html;. If your server has no such rule, deep routes break. The same applies to an SPA generated by React Router's framework mode with ssr: false in react-router.config.ts.
For pages that need SEO and content on first load, a pure SPA is limited: see SSR vs SSG vs static and how to host a Next.js app.
How do you use environment variables in React?#
Vite only exposes variables prefixed with VITE_ to your code, read through import.meta.env. In a .env.production file:
VITE_API_URL=https://api.example.comconst res = await fetch(`${import.meta.env.VITE_API_URL}/products`);The value is written into the JavaScript at build time. Changing the variable later requires a new build, and any visitor can read the value in the page's code. That is why the Vite docs say VITE_* variables should not contain sensitive information such as API keys. Secrets belong in a backend (see how to host an API), and the front end calls that backend. For the general concept, read what environment variables are.
How do you publish the dist folder?#
The exact steps depend on where you host, but the principle is the same: send the contents of dist/ (not the folder itself) to a static server, with index.html at the root. Check before publishing:
npm run buildfinished without errors.npm run previewopens the app and navigation between routes works.- The fallback to
index.htmlis configured on the server. - No
.envor secret ended up indist/, because everything there is public.
After deploying, open a deep route directly in the address bar (for example, /about) and reload. If it loads, the fallback is right. For the full cycle, see what is deployment and how to host a website.
How do you add a custom domain?#
Once the app responds at the provider's address, point your domain at it using the DNS records the provider tells you to create (usually a CNAME for subdomains such as www). HTTPS is normally issued automatically once DNS propagates. The steps are in how to point a domain to your app and how to use a custom domain.
What are the common mistakes?#
- Blank screen in production: Vite's
baseis wrong for where the site lives, so files under/assetsreturn 404. Check the browser console and the Network tab. - 404 on reload: the fallback to
index.htmlis missing, as explained above. import.meta.env.VITE_Xisundefined: the variable lacks theVITE_prefix, or it was set after the build.- Old site after publishing: the browser or CDN cached
index.html. Files inassets/carry a hash in their name, so onlyindex.htmlshould have short caching. See what is Cache-Control.
How to do this on Square Cloud#
A React app built with Vite runs on Square Cloud's static sites runtime. You run npm run build and upload the contents of dist/ as a ZIP in the dashboard; the CLI, GitHub and the VS Code extension are the other ways to deploy.
For the route fallback, rename dist/index.html to vite.html before zipping: the static runtime answers every path that is not a file with vite.html, so reloading /about works. The steps are in the documentation's React tutorial.
The squarecloud.app configuration file is optional: the dashboard generates it at deploy time with the main file and memory, or you can write it yourself at the root of the project. Websites need at least 512 MB of RAM.
Every web application gets a *.squareweb.app subdomain with HTTPS, on every plan. A custom domain is available from Standard: you create the DNS records the dashboard shows at your registrar, and TLS is automatic. The domain stays registered in your name. Plans are billed in reais, and Hobby starts at R$12.49 per month. For more detail, see domains and site hosting.
Website hosting
Static and SSR sites with automatic TLS and a global CDN
FAQs
Do I need a Node.js server to host a React app?
Not for a single-page app built with Vite. The build produces plain HTML, CSS and JavaScript that any static file server can deliver. You only need Node.js if you use server-side rendering (SSR).
Why does my React app return 404 when I reload a route?
Because the route only exists in the browser's JavaScript. The server looks for a file at that path, finds none and answers 404. The fix is to make the server return index.html for paths that are not files.
Can I keep API keys in React environment variables?
Not secret keys. Variables like VITE_API_URL are embedded in the build's JavaScript, and any visitor can read them. Keep secrets in a backend and expose only public values to the front end.
Is Create React App still a good way to start a new app?
It is not recommended. The React team deprecated it for new apps in February 2025. The React docs recommend a framework such as Next.js or React Router; to assemble your own project, use a build tool such as Vite.
More in Websites
See all 20 articles- What is a static site (and how to host one)A static site is a set of ready-made HTML, CSS and JavaScript files served the same to every visitor. Learn how it works, when to use one and how to host it.
- How much does it cost to host a website?Hosting a website costs anywhere from nothing to a few dozen dollars a month. See what goes into the bill: hosting, domain, traffic and extras, with real limits.
- How much does static site hosting cost?Hosting a static site costs between zero and a few dozen dollars a month in most cases. See how traffic sets the price, with step-by-step math.
- How much does it cost to host a Next.js app?Hosting a Next.js app costs from zero with a static export to tens of dollars a month with a server. See the billing models, traffic math and hidden costs.