Skip to main content
Software Engineer

How does Vite work? What happens after running npm run dev

Sep 21, 2026  |  2 min read
How does Vite work after running npm run dev
Saurabh Jaybhaye
Software Engineer

Part 1 explored why Vite was created and how it changed the traditional frontend development experience. This part goes one step deeper into what actually happens after running npm run dev.

The Vite request lifecycle, step by step

This part follows the journey of a browser request from the moment this command runs:

npm run dev

This blog covers:

  • How npm run dev starts the Vite development server
  • How Vite serves the application's HTML
  • How the browser loads JavaScript using Native ES Modules
  • How Vite transforms JSX before sending it to the browser
  • How Vite handles dependencies such as React
  • What does dependency pre-bundling mean
  • How Vite builds and maintains the module graph
  • What happens when you change a file
  • What a Hot Module Replacement (HMR) updates the browser
  • How Vite's development workflow differs from production

The goal is to understand what happens between running npm run dev and seeing a React application in the browser.

How Vite uses native ES modules

How does Vite use native ES modules?

Modern browsers understand JavaScript modules natively. For example.

// main.js

import { greet } from './greet.js'

greet()

The browser sees:

import { greet } from './greet.js'

and knows it needs greet.js.

Conceptually,

greet.js

This is native ES modules, and this capability is one of the foundations of Vite's development experience.

How Vite handles a browser request

Imagine a simple React application

src/
├── main.jsx
├── App.jsx
└── components/
    └── Header.jsx

The entry file contains

// main.jsx

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'

ReactDOM.createRoot(document.getElementById('root')).render(
  <App />
)

App.jsx imports Header.jsx, so the dependency graph already looks something like:

dependency graph
 
Now run:

npm run dev

and open:

http://localhost:5173

These are the two steps by which Vite handles a browser request

Step 1: How Vite serves the HTML

How Vite serves the HTML

The browser first requests the application’s HTML. A simplified version contains:

<script type="module" src="/src/main.jsx"></script>

That type="module" is important. It tells the browser: "This is an ES Module. Handle its imports natively."

The browser now requests: /src/main.jsx

Vite's development server receives the request. But there's a problem. Browsers don't natively execute JSX syntax, so Vite transforms JSX into JavaScript before serving the module to the browser.

For example:

<App />

isn't valid JavaScript that the browser can execute directly. So Vite transforms the file before sending it back. The flow is roughly:

Vite file transformation
 
At this point, Vite has transformed only the module that was requested. The rest of the application hasn't been bundled.

Step 2: How the browser loads the application

How the browser loads the app with Vite

The transformed main.jsx still contains imports such as

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'

The browser now knows it needs these modules, so it requests App.jsx.

App.jsx imports:

import Header from './components/Header.jsx'

So, the browser requests Header.jsx too.

The process looks like:

Header.jsx_browser_request

The browser loads the application's module graph as it discovers imports. This is the key difference from the traditional "bundle everything first" development model.

How Vite handles React and bare module imports

This is where an interesting problem appears. The code contains:

import React from 'react'

This is different from:

import App from './App.jsx'

The second one points to a relative file. react, on the other hand, is a bare module import. The browser doesn't know that it should look inside: node_modules/react

Vite needs to resolve and optimize these dependencies, and this is where dependency pre-bundling comes in.

How Vite pre-bundles dependencies

When the dev server starts, or when it discovers that dependency optimization is needed, Vite analyzes and pre-bundles dependencies for browser-friendly consumption. For example:

node_modules/
├── react
├── react-dom
├── lodash
└── ...

Vite can pre-bundle these dependencies using esbuild. The important distinction is pre-bundling applies to third-party dependencies. The application's own source files are still served one module at a time.

Think of it as:

Vite can pre-bundle these dependencies

So the common claim that "Vite doesn't bundle during development" needs a small correction: Vite avoids bundling the entire application during development, while still pre-bundling dependencies when necessary.

Understanding Vite's module graph

By now, Vite understands how the modules in the application are connected:

Vites_Module_Graph

This is the module graph. Vite uses it to track relationships between modules, which becomes especially useful when something changes.

What happens when a file changes in Vite?

Imagine changing:

function Header() {
  return <header>My Application</header>
}

to:

function Header() {
  return <header>My Awesome Application</header>
}

and saving the file. What should happen? Vite detects the change. Because it knows the module graph, it can identify the affected module and communicate the update to the browser. This is where Hot Module Replacement (HMR) comes in.

Hot module replacement

The simplified flow is:

Hot module replacement

Vite maintains a connection with the browser, commonly using a WebSocket, so it can notify the browser when a module changes.

A traditional bundler rebuilds on every save and slows down as the project grows, while Vite re-transforms only the changed module and sends it straight to the browser.

Vite development experience

That's a big reason Vite feels so fast during development.

Understanding Vite's development architecture

Frontend development is often pictured as one long build step that produces a bundle, while Vite's model puts the browser in charge of requesting modules as it needs them.

Vite's development architecture

In this model, the browser requests modules as it needs them, and Vite takes advantage of that native capability. That's the important architectural shift.

Development vs production

There is one important distinction left. If Vite can serve modules individually during development, what happens when the application is deployed?

Production users shouldn't have to request hundreds or thousands of individual modules. That's why the development and production workflows are different.

Development looks like:

Vite in Development

While production looks more like:

Vite in Production

Alongside the development server, Vite also provides the production build pipeline. That's where the next part of the story begins.

A simple mental model for how Vite works

Ask "What happens when you run npm run dev with Vite?" and the short answer is "Vite starts a development server." A more complete answer looks like this:

how Vite works

And suddenly, the "magic" behind Vite's development experience doesn't look quite so magical anymore.

Key takeaways

  • Running npm run dev starts a development server that serves the application module by module as the browser requests it.
  • The type="module" script tag in index.html lets the browser load the application using native ES modules.
  • Vite transforms JSX into plain JavaScript on request.
  • The browser builds out the module graph by following imports, from main.jsx to App.jsx to Header.jsx.
  • Vite resolves bare imports like react and pre-bundles third-party dependencies into browser-friendly modules.
  • Because Vite tracks the module graph, a file change is traced to the affected module and pushed to the browser over a WebSocket through HMR.
  • Development serves individual modules for speed, while production bundles the application into optimized output for deployment.

Coming up in part 3

Part 3 focuses on vite.config.ts, including plugins, aliases, environment variables, server configuration, build options, and how this configuration file controls much more than the dev server.

Planning a move to Vite or modernizing a frontend build pipeline? Talk to Opcito's engineering experts.

Comments

Ready to transform your FinTech Application?

Explore our VAPT as a Service portfolio or speak with our team to discuss your security testing requirements.