How To Deploy A Vite App From Its Dist Folder

How to deploy a Vite app: what npm run build puts in dist, why only that folder ships, the routing setting that saves deep links, and three ways to go live.
The OxyPages Team · · 8 min read
You ran npm run build, a dist folder appeared, and now you're wondering which of the files in your project actually go online. Most guides answer by walking you through a git provider, a dashboard and a build pipeline before you see a live link.
You can skip most of that. To deploy a Vite app, you put the contents of dist on a web server. That's it. Everything else in your project folder, node_modules included, stays on your laptop.
I scaffolded a fresh React app with create-vite on 8 November 2026 to check what that folder really holds. The build took 171 milliseconds and produced eight files, under 300 KB in total. The node_modules folder next to it was about 55 MB. A rerun on 13 November with Vite 8.3.8 produced the same eight files, byte for byte.
That ratio is the whole story. Below is what's in the folder, why only the folder ships, the one setting that decides whether links survive a refresh, and three ways to get it live, from a drag and drop to a host that rebuilds on every push. The wider React hosting picture is covered elsewhere; this one stays on the folder.
TL;DR: A Vite build is plain static files. Run npm run build, then upload what's inside dist (not the folder around it, and never node_modules). If your app has client-side routes, the host must serve index.html for paths it can't find, or deep links 404 on refresh. Pick one of three routes: drag the folder onto a host, send it from a CLI or an AI agent, or connect the repository so the host builds on every push. If your Vite app renders on a server, none of this applies and you need a host that runs code.
What Does npm run build Put In dist?
The dist folder is your finished website: HTML, CSS, JavaScript and images, already bundled and named for caching. Vite's guide to deploying a static site puts it plainly: by default, the build output goes to dist, and that's the folder you deploy.
Here's the real listing from my test build, Vite 8.3.6 with the React template:
dist/
index.html 0.45 kB
favicon.svg 9.5 kB
icons.svg 5.0 kB
assets/
index-DSXtc1GT.js 222.4 kB (69.3 kB gzipped)
index-DykytF2W.css 4.1 kB
hero-CLDdwZDr.png 13.1 kB
react-CHdo91hT.svg 4.1 kB
vite-BF8QNONU.svg 8.7 kB
Three things are worth noticing.
index.html is tiny. Its body is one empty <div id="root"> plus a script tag. Your whole app lives in that one JavaScript bundle, React included.
The random-looking part of each name, like DSXtc1GT, is a content hash. Change your code and the hash changes, so a host can tell browsers to cache these files forever and still ship updates instantly.
Files in your public folder, like favicon.svg, are copied across as they are, with their names untouched.
Why You Deploy Only The dist Folder

Your source files are instructions for Vite, not a website. A browser can't read JSX, can't resolve import React from 'react', and has no idea what a .tsx file is. The build turns all of that into the files above.
So when you upload, upload what's inside dist:
- Not the project folder. Its
index.htmlpoints at/src/main.jsx, source code no browser can run, so you get a blank page. - Not
node_modules. It's build-time tooling. My test app had 55 MB of it, and nothing in it is needed once the build has run. - Not
src. Shipping it just publishes your source code next to the bundle. - Not the
distfolder itself, if the host wants a zip. Zip the contents, soindex.htmlsits at the top of the archive. Zipping the contents rather than the folder covers that trap in detail.
One check before you upload: run npm run preview. It serves dist at http://localhost:4173, so you see the built version, not the dev server. Vite's CLI reference for vite preview is clear that it's for checking locally and isn't meant to be a production server. One thing it hides: preview serves index.html for every path, so it won't show you a refresh 404. Test that on the live link.
The One Setting That Breaks Deep Links
If your app uses React Router, Vue Router or any client-side router, the host has to send index.html for paths that don't match a file. That's called a single-page app fallback.
Without it, the home page works and clicking around works, but refreshing on /settings or opening a shared link to it returns a 404. The browser asked the server for /settings, and there's no such file in dist. Only the JavaScript knows that route exists.
Every major static host can fix this, each with its own switch or file, and it's one line of config on most of them. The guide to fixing a 404 on refresh has the exact setting for each host. Do it before you share a link, not after someone tells you it's broken.
Two related gotchas come up right after this one. If the app lives in a subfolder rather than at the root of a domain, set Vite's base path, or the page goes blank. And any VITE_ environment variable is baked into the bundle as plain text, so never put a secret in one. My test build proved it: a VITE_ value showed up word for word in the JavaScript, and a variable without the prefix came out as undefined.
Three Ways To Deploy A Vite App

There are three real routes, and they differ mostly in who runs the build and when.
| Upload the folder | Send it from a CLI or agent | Push to deploy | |
|---|---|---|---|
Who runs npm run build | You, on your machine | You, or the agent | The host, on its servers |
| What you hand over | The contents of dist | dist, usually zipped | The repository |
| How you update | Upload again | Run the command again | Push a commit |
| Setup | None | A CLI install, or none for some agent routes | Connect GitHub and set the build |
| Best for | A first deploy, a demo, a one-off | Scripts and AI coding agents | Anything you change weekly |
Route 1: Upload The Folder
This is the quickest way to see your app live, and most static hosts offer it. Netlify Drop takes a dragged folder. Cloudflare Pages has a direct upload. GitHub Pages wants the files committed to a repository, which is more steps than it sounds.
On OxyPages it's New Project, then Upload Files, then drag in dist or a zip of its contents. If the app has client-side routes, switch on Single-Page App Mode in the same step, or later in the project's Settings.
The catch with this route is that every update is a manual rebuild and re-upload. Fine for a portfolio you touch twice a year. Tedious for a project you're actively shipping.
Route 2: Send It From A CLI Or An AI Agent
Every big host has a command that uploads a built folder: netlify deploy --dir=dist, vercel deploy, wrangler pages deploy dist. Each needs its CLI installed and, for anything permanent, an account.
If you're working with an AI coding agent like Claude Code, the agent can do the whole thing in one go. Here's the request it sends to the same host as the upload example above, with no account and no CLI:
npm run build
(cd dist && zip -r ../dist.zip .)
curl -sS -X POST https://api.oxypages.com/drops/app \
-H "X-Oxy-Agent: my-agent" \
-F "file=@dist.zip"
The reply carries a live HTTPS link that lasts 30 minutes and a claim link that keeps it permanently in a free account. Claiming moves the app to its own new address. The same request works from any agent that can run a shell command, and the agent publishing guide has the details.
Send the built output, not the source. A folder with a package.json and no build output gets refused with a message saying exactly that, because a no-account drop never runs your build for you.
Route 3: Push To Deploy
For anything you'll keep changing, connect the repository and let the host build it. You set the build command (npm run build) and the output folder (dist) once, and every push to your main branch rebuilds and redeploys. Most hosts also give each pull request its own preview link.
Netlify, Vercel and Cloudflare all detect Vite automatically, and so do newer hosts that build from GitHub. Each one counts build minutes against a monthly allowance, so check what yours includes before you push forty times a day.
This is the route I'd use for any project I expect to touch more than once a month. The first setup costs ten minutes and every deploy after that costs a git push.
When This Isn't A Static Deploy
Everything above assumes your Vite app is a client-side app, which is what create-vite gives you. Some Vite projects aren't.
If you've added a server layer, like Nitro, Vike in server mode or TanStack Start with server functions, your build produces code that has to run on a server. No static host can run it, and that includes OxyPages, which serves files and never runs your code. Use Vercel, Netlify or Cloudflare Workers for those, because they run server functions and a static host doesn't.
A quick test: if dist contains only HTML, CSS, JavaScript and assets, it's static. If your build also writes a server folder or an entry file meant for Node, it isn't.
What To Do Next
- Run
npm run buildand opendist. Check thatindex.htmlis at the top. - Run
npm run previewand click through every route athttp://localhost:4173. - Decide on the route: upload for a one-off, CLI or agent for scripts, push to deploy for anything ongoing.
- If you have client-side routes, turn on your host's single-page app fallback before you share a link.
- Open the live link, go to a deep route, and hit refresh. If it loads, you're done.
If you want the folder live without connecting anything first, put your dist folder online with OxyPages and claim it when you're happy with it. For a portfolio, a demo or a side project, start a free account, no card needed: 25 MB of storage covers a build like the one above many times over.
FAQ
Do I Need Node.js On The Server To Deploy A Vite App?
No. Node runs on your machine during the build. Once npm run build finishes, dist is ordinary files, and the server's only job is to hand them to browsers. That's why almost any host will serve one.
How Big Is A Vite Build?
Smaller than people expect. My fresh React test app built to eight files and under 300 KB, against about 55 MB of node_modules it needed to get there. The JavaScript bundle was 222 KB, or 69 KB once a host compresses it.
Should I Commit The dist Folder To Git?
Usually not. Add dist to .gitignore and let the host build it from source, so the repository holds code and the host holds output. The exception is a host with no build step, like plain GitHub Pages without Actions, where you commit the built files on purpose.
Can I Deploy A Vite App Without A Repository?
Yes. Upload the contents of dist by dragging them onto a host, or send them with a single command from a CLI or an AI agent. A repository only becomes worth it once you're deploying often enough that rebuilding by hand gets old.