localhost:5173 / dev-server-startup
What happens between npm run dev and the page on localhost:5173
Starting the Vite dev server takes five steps: npm resolves the vite binary, Vite reads the config, dependencies are pre-bundled, the server listens on 5173, and the browser opens an HMR WebSocket on the first request.
The five steps
Every symptom on this page belongs to exactly one of them. Finding the step is most of the work.
- 1. npm resolves the vite binary from node_modules/.bin and runs it.
- 2. Vite loads vite.config.js and merges it with CLI flags.
- 3. The optimizer crawls your entry points and pre-bundles the dependencies it finds.
- 4. The server binds to a port — 5173 unless it is taken or configured otherwise.
- 5. The first request loads the app, and the client opens a WebSocket for HMR.
What the successful start looks like
Vite prints the address block once it is listening. Until that block appears, nothing is serving on 5173.
$ npm run dev VITE v8.1.5 ready in 412 ms ➜ Local: http://localhost:5173/ ➜ Network: use --host to expose ➜ press h + enter to show help
Step 1 — npm resolves the binary
The dev script runs vite, and npm looks for it in node_modules/.bin. If the package was never installed, or the install was interrupted, the shell reports a missing command and nothing else happens. This is the failure that never reaches 5173 at all, and it is the most searched one.
Step 2 — the config is read
Vite loads vite.config.js from the project root unless -c points elsewhere. CLI flags win over the config file, which is why a --port on the command line beats server.port. A framework wrapper such as Nuxt or SvelteKit adds a third layer and may override both.
Step 3 — dependencies are pre-bundled
Vite crawls your source for bare imports and converts those packages to ESM once, writing the result to node_modules/.vite. On a cold start this is the slowest step. On later starts it is skipped, because the cache is keyed to the lockfile, the patches directory, the relevant config fields and NODE_ENV — change one of those and the step runs again.
Step 4 — the server binds to a port
Vite listens on 5173 by default and on localhost only. If 5173 is taken, it moves to the next free port and prints the one it actually got, which is why the terminal sometimes says 5174. If host is left at the default, the address answers on your machine and nowhere else.
Step 5 — the HMR socket opens
Once the page loads, the client opens a WebSocket back to the server. Edits travel over that socket. If it fails to connect, the page still renders, but nothing updates until you reload by hand — so a page that loads and never changes is a step-5 problem, not a step-3 one.
Which symptom belongs to which step
- Command not found, or the script exits instantly — step 1.
- Your port or proxy setting appears to be ignored — step 2.
- Long cold start, or a reload right after the first load — step 3.
- The terminal says 5174, or the address answers nowhere but on your machine — step 4.
- The page loads once and never updates again — step 5.
- The page loads and stays blank — none of them: the server answered, so the problem is in your application code.
# faq
Questions
Why does the first start take so much longer than the next one?
Because dependency pre-bundling only runs on a cold start. Later starts reuse node_modules/.vite until something the cache is keyed to changes.
Is vite dev different from npm run dev?
No. The dev script calls the same binary. vite, vite dev and vite serve all start the dev server.
The terminal shows a URL but the browser will not open it.
Then the server is listening and the problem is between the browser and the address — check whether the printed port really is 5173 and whether you typed https on a server serving http.
Does the dev server build my site?
No. It serves source files over native ESM and transforms them per request. The build is a separate command, and it is served on port 4173 by vite preview.
# next
Related
# sources
Checked against the Vite documentation on 2026-09-11.