Deploy
Deploy a React + Vite App for Beginners
React + Vite is one of the most common stacks in AI-generated frontends — especially from tools like Lovable, Bolt, and Cursor templates. Unlike Next.js, a standard Vite React app builds to static files in a dist/ folder. There is no Node server to run in production unless you add one. This guide explains what actually gets deployed, how to publish on Vercel or Netlify, why VITE_ environment variables behave differently from server env vars, and how to wire your frontend to a backend hosted elsewhere.
Updated July 2026 · 13 min read
Written for AI makers using tools like Cursor, Bolt, Lovable, Replit, Claude, and ChatGPT.
Quick answer
Run npm run build to produce a dist/ folder of static HTML, JS, and CSS. Deploy that folder on Vercel or Netlify (build command: npm run build, output directory: dist). Set VITE_ prefixed env vars in the host dashboard before building — they are baked into the bundle at build time. If your app calls an API, deploy the backend separately and point VITE_API_URL to its production URL.
Best for
React + Vite SPAs from Lovable, Bolt, Cursor, or create-vite
Deploy to
Vercel or Netlify (static hosting — no Node server required)
Main risk
Secrets or localhost API URLs baked into the browser bundle
What gets deployed
The dist/ folder: static HTML, JS, CSS, and assets
Who this is for
- You have a React app created with Vite (npm create vite@latest or an AI tool) and want it online
- You are unsure whether you need a server or just static hosting
- Your deployed app shows a blank page, wrong API URL, or old config after changing env vars
What you'll need
- React + Vite project on GitHub
- Vercel or Netlify account (both have free tiers for static sites)
- If your app uses a backend API, that API deployed somewhere with a public URL
What Vite actually builds
Vite is a development server and build tool — not a hosting platform. During development, npm run dev serves your React code with hot reload. For production, npm run build compiles everything into the dist/ directory: index.html, JavaScript bundles, CSS, and static assets like images.
That dist/ folder is a static site. A CDN serves the files to browsers. There is no server executing your React code on each request — the user's browser downloads the JS and runs it. This is called a Single Page Application (SPA) when client-side routing handles navigation after the first load.
Because the output is static, you cannot hide secrets in frontend code. Anything referenced in your React components or in import.meta.env ends up in the downloaded bundle. Plan accordingly: public API base URLs are fine; private keys are not.
If your AI tool added a backend in the same repo (Express, FastAPI, etc.), that backend does not deploy automatically with a static Vite build. You need a separate deploy for the API — usually Railway, Render, or Fly.io.
Prepare your repo for static deploy
Before connecting a host, run npm run build locally and confirm dist/ appears without errors. Fix TypeScript issues now — the host build will fail the same way.
Check vite.config.ts for a base path if your app will not live at the domain root. GitHub Pages and some subpath deploys need base: '/repo-name/'. Vercel and Netlify at the root domain usually need base: '/' (the default).
For client-side routing (React Router), you need a fallback rule so every path serves index.html. Vercel and Netlify both provide SPA redirect configs — Vite templates sometimes include a public/_redirects (Netlify) or vercel.json file. If deep links 404 in production, add the fallback.
Commit .env.example with every VITE_ variable your app reads. Add .env to .gitignore. Never push secrets — VITE_ vars are public once built.
npm run buildcreatesdist/successfullyvite.config.tsbase path matches your deploy URL- SPA fallback configured for React Router deep links
.env.examplelists allVITE_variables
Deploy settings
Deploy on Vercel
Sign up at vercel.com, click Add New → Project, and import your GitHub repository. When Vercel asks for framework settings, choose Vite or set manually: Framework Preset Vite, Build Command npm run build, Output Directory dist, Install Command npm install (or leave default).
Add environment variables before the first deploy. Any variable your code reads via import.meta.env.VITE_SOMETHING must be set here with the VITE_ prefix. Example: VITE_API_URL=https://your-api.railway.app
Click Deploy. Vercel runs the build on its servers, uploads dist/ to its CDN, and gives you a *.vercel.app URL. Open it in incognito and test navigation to routes like /dashboard — not just the homepage.
Subsequent git pushes to main trigger automatic redeploys. Changing env vars requires a new deploy to rebuild the bundle — editing them alone does not update an already-built site.
- Framework: Vite
- Build command:
npm run build - Output directory: dist
- SPA fallback:
vercel.jsonrewrites to /index.html
Deploy settings
Deploy on Netlify
Sign up at netlify.com and click Add new site → Import an existing project → connect GitHub. Select your repository.
Build settings: Build command npm run build, Publish directory dist. Netlify's Vite detection often fills these automatically.
Go to Site configuration → Environment variables and add each VITE_ variable. Deploy the site. Netlify shows a *.netlify.app URL when the build succeeds.
For React Router SPAs, add a _redirects file in public/ with the line /* /index.html 200 — Vite copies public/ contents into dist/ on build. Alternatively, use a netlify.toml with [[redirects]] from /* to /index.html with status 200.
Netlify and Vercel are interchangeable for basic Vite SPAs. Pick whichever dashboard you prefer — the build output is identical.
- Build command:
npm run build Publish directory: dist- SPA fallback:
public/_redirectsornetlify.toml
Key concept
VITE_ environment variables (build-time, not runtime)
Vite only exposes variables prefixed with VITE_ to your client code via import.meta.env.VITE_API_URL. Unprefixed variables are ignored in the browser bundle — a safety feature, not a hosting quirk.
Critical difference from Next.js server env vars: VITE_ values are embedded at build time. When you change VITE_API_URL on Netlify, you must trigger a new build. The old bundle still contains the previous URL until redeployed.
Never put private keys in VITE_ variables. Stripe secret keys, OpenAI API keys, and database passwords belong on your backend server, not in React. If your AI template calls OpenAI directly from the browser with a VITE_OPENAI_KEY, refactor to a backend proxy before launch.
For local development, .env holds your values. For production, the host dashboard holds them. Keep .env.example updated so you do not forget a variable on deploy day.
Access pattern in code: const apiUrl = import.meta.env.VITE_API_URL. TypeScript projects often add a vite-env.d.ts reference for ImportMetaEnv.
- Only
VITE_prefixed vars reach the browser - Values are baked in at build time — redeploy after changes
- Never put secrets in
VITE_variables
Watch out
Connecting to a separate API backend
Most real apps are not frontend-only. Your React UI talks to an API for auth, data, and payments. That API runs on a different host — Railway for Django or FastAPI, Render for Node Express, Supabase for hosted Postgres + edge functions.
Set VITE_API_URL (or whatever name your code expects) to the production API base URL, including https:// and no trailing slash unless your fetch calls assume one. Rebuild the frontend after setting it.
CORS must allow your frontend domain. On the backend, configure Access-Control-Allow-Origin for https://your-app.vercel.app (or Netlify URL). AI backends often allow only http://localhost:5173 — update before launch.
Auth tokens: if using JWT or session cookies across domains, confirm cookie SameSite settings and that your auth provider lists the production frontend URL as an allowed origin.
Split deploy workflow: push backend first, get the API URL, add it as VITE_API_URL on the frontend host, then build and deploy the frontend. Testing with localhost API URLs baked into production bundles is a top beginner mistake.
- Deploy the API first — get a public HTTPS URL
- Set
VITE_API_URLon the frontend host, then rebuild - Update CORS / auth callbacks for your live frontend domain
Common React + Vite deploy errors
Blank white page after deploy: open browser dev tools → Console. Usually a JavaScript error, wrong base path in vite.config, or a missing env var that makes import.meta.env.VITE_SOMETHING undefined and crashes the app.
404 on refresh for /dashboard or other routes: missing SPA fallback. Add _redirects or vercel.json rewrite to index.html.
API calls still go to localhost: VITE_API_URL was not set on the host before build, or you forgot to redeploy after changing it. Search your built dist/assets/*.js for localhost to confirm what got baked in.
CORS error in browser console: backend does not allow your frontend origin. Fix ALLOWED_ORIGINS or CORS middleware on the API server.
Old content after deploy: hard refresh (Cmd+Shift+R) or check you deployed the right branch. CDN caching is aggressive but usually not the first suspect.
Vite SPA vs Next.js — which deploy path?
If your project has vite.config.ts and no next dependency, follow this guide. If it has next in package.json, use the Next.js deploy guide instead — the env var rules and hosting defaults differ.
Vite SPAs are simpler to host (static files only) but lack server-side rendering unless you add SSR plugins. For SEO-heavy marketing pages, Next.js or a prerender step may matter. For internal tools and dashboards, static Vite deploy is often enough.
AI tools pick different stacks. Lovable and many Bolt exports use Vite + React. Cursor templates vary. Check package.json before choosing a host preset.
Vercel vs Netlify
| Criteria | Vercel | Netlify |
|---|---|---|
| Build command | npm run build | npm run build |
| Output directory | dist | dist (Publish directory) |
| SPA fallback | vercel.json rewrites → /index.html | public/_redirects or netlify.toml |
| Good for | Vite preset + GitHub auto-deploy | Simple static sites + easy redirects file |
Step-by-step
- 1
Step 1
Build locally and verify dist/
Run
npm run build. Opendist/index.htmlstructure — confirm assets compiled. Fix errors before connecting a host. - 2
Step 2
Push to GitHub
Ensure
.envis gitignored and.env.exampledocumentsVITE_vars. Push via GitHub Desktop, Cursor, or git. - 3
Step 3
Deploy backend first (if applicable)
If your app needs an API, deploy it on Railway or similar. Note the production URL for the next step.
- 4
Step 4
Import on Vercel or Netlify
Build command:
npm run build. Output/publish directory: dist. AddVITE_env vars includingVITE_API_URLif needed. - 5
Step 5
Test routes and API calls live
Open the live URL in incognito. Navigate to inner routes, log in, and confirm network requests hit your production API — not
localhost.
Common mistakes
Setting API URL only in local
.envand expecting production to pick it upWhat happens: The live site still calls
localhostor an old URL — login and data features fail in production only.How to fix it: Add
VITE_API_URLin Vercel or Netlify env settings, then trigger a full rebuild and redeploy.Putting secret API keys in
VITE_variablesWhat happens: Anyone can open DevTools, inspect the JS bundle, and copy your keys.
How to fix it: Move secret calls to a backend. Only public URLs and publishable keys belong in
VITE_vars.Wrong output directory (build instead of dist or vice versa)
What happens: The host deploys an empty folder or the wrong files — you get a 404 or a blank site.
How to fix it: Vite outputs to
dist/by default. Confirm invite.config.ts— do not guess.Forgetting SPA fallback for React Router
What happens: Homepage works, but refreshing /dashboard or sharing a deep link returns 404.
How to fix it: Add
public/_redirectswith /* /index.html200 for Netlify, or equivalentvercel.jsonrewrites.Deploying only the frontend when the repo also contains a backend
What happens: The UI loads, but every API call fails because nothing is serving the server code.
How to fix it: Deploy the API separately on Railway/Render. Static hosts only publish the
dist/folder.
Copy-ready prompt
Prompt: prepare React + Vite app for static deploy
Paste this into Cursor, Claude, ChatGPT, Bolt, Lovable, or your AI coding tool.
Ready to paste
I am deploying this React + Vite app to Vercel (or Netlify). Audit it for production. 1. Confirm npm run build works and output goes to dist/. 2. List every import.meta.env.VITE_* variable used. Create or update .env.example. 3. Find hardcoded localhost URLs in fetch calls or axios config — replace with env vars. 4. Check if React Router needs SPA fallback (_redirects or vercel.json). 5. Verify vite.config.ts base path is correct for root-domain deploy. 6. Identify any secret API keys exposed in frontend code and move them to a backend. 7. If there is a backend in this repo, explain how to deploy it separately and what VITE_API_URL should be. 8. List CORS or auth callback changes needed for production. Do not commit real secrets.
Paste this into Cursor, Claude, ChatGPT, Bolt, Lovable, or your AI coding tool.
Ready check
Before you deploy
Work through these checks before you connect a host.
npm run buildsucceeds —dist/folder created.envgitignored —.env.examplelists allVITE_variables- Backend API deployed with public URL (if app uses an API)
VITE_API_URLset on host before build- Build command
npm run build, output directory dist - SPA fallback configured for client-side routes
- CORS allows frontend production domain on API
- Live site tested — inner routes and API calls work in incognito
Frequently asked questions
- Do I need a server to host a Vite React app?
- No for the frontend itself — static hosting on Vercel, Netlify, or Cloudflare Pages serves
dist/. You only need a server if you have a separate backend API, which deploys elsewhere. - Why do my env var changes not appear after deploy?
VITE_variables are baked in at build time. Changing them on the dashboard requires triggering a new build, not just saving settings.- Vercel or Netlify for Vite?
- Both work well for static SPAs. Settings are nearly identical: build command
npm run build, output dist. Choose based on which dashboard you prefer. - What is the difference between VITE_ and NEXT_PUBLIC_?
- Both mark client-visible env vars.
VITE_is for Vite projects (import.meta.env).NEXT_PUBLIC_is for Next.js (process.env). Do not mix them — use the prefix your build tool expects. - My Lovable or Bolt app won't connect to the API — what now?
- Deploy the backend first, set
VITE_API_URL(or the variable your template uses) on the frontend host, rebuild, and fix CORS on the API to allow your frontend domain.