A development server can say it is ready before your browser has finished loading the page. A file change can feel instantaneous but still trigger a full reload. And a fast local feedback loop tells you surprisingly little about the JavaScript you will ship.

Those distinctions matter when deciding whether to use Vite. It is a build tool with two related jobs: serving source modules efficiently while you develop, and producing deployable assets when you build for production. Its biggest architectural advantage is that it does not require an upfront application bundle just to start serving source files during development. That is useful, but it is not a guarantee of faster page loads or faster builds in every project.

The original Vite approach combined native browser ES modules with dependency pre-bundling. In Vite 8, released in March 2026, the underlying toolchain changed: Rolldown and Oxc replaced the older esbuild-and-Rollup combination. The distinction between an on-demand development server and an optimized production build still matters. See Vite's architecture and the Vite 8 release notes.

What does the browser actually request?

Imagine a small app with an entry module that imports a view and a rarely used report feature.

In a traditional development setup that builds a bundle before serving the app, the tool processes the relevant module graph upfront. Vite instead serves application source as browser-requested ES modules, transforming syntax such as TypeScript or JSX when necessary.

A simplified request flow is:

Browser requests /index.html
  -> Browser requests /src/main.js
     -> Vite resolves and transforms imports as needed
        -> Browser requests imported modules
           -> A module behind import() is requested when that path runs

This is not "zero work at startup." Vite still discovers dependencies, resolves imports, and may transform files. Packages imported by name, such as react, also need special handling because a browser cannot resolve a bare package name on its own. Vite rewrites those imports to browser-usable URLs and pre-bundles dependencies for development, including CommonJS compatibility. In Vite 8, Rolldown performs that optimization, not esbuild. See dependency pre-bundling.

For a typical project this can improve startup and edit feedback because Vite does not need to rebuild the entire source bundle on every change. The tradeoff is visible in larger module graphs: an unbundled dev page can make many module requests, and dependency discovery or linked packages can still cost time. "Server ready" and "page interactive" are separate measurements.

A small request experiment you can inspect

The following is a minimal Vite 8 vanilla JavaScript example. It demonstrates deferred module loading: the browser requests main.js at startup and reaches report.js only after a click. It does not demonstrate Hot Module Replacement or state preservation. The JavaScript behavior was checked with an isolated Node.js test; the Vite browser and build steps below are an inspection procedure, not an executed Vite benchmark.

Use Node.js 20.19+ or 22.12+ (or a newer compatible major version), as required by Vite 8. Create a directory and install Vite:

mkdir vite-request-lab
cd vite-request-lab
npm init -y
npm install --save-dev vite@^8
mkdir src

Create index.html:

<!doctype html>
<html lang="en">
  <head><meta charset="UTF-8" /><title>Vite request lab</title></head>
  <body>
    <button id="load-report" type="button">Load report</button>
    <pre id="report" role="status" aria-live="polite">Not loaded</pre>
    <script type="module" src="/src/main.js"></script>
  </body>
</html>

Create src/main.js:

const button = document.querySelector('#load-report')
const output = document.querySelector('#report')

button.addEventListener('click', async () => {
  button.disabled = true
  output.textContent = 'Loading...'

  try {
    const { formatReport } = await import('./report.js')
    output.textContent = formatReport(['request finished', 'feature ready'])
  } catch {
    output.textContent = 'Report unavailable. Reload and try again.'
  } finally {
    button.disabled = false
  }
})

Create src/report.js:

export function formatReport(lines) {
  return lines.join('\n')
}

Start the dev server:

npx vite

Open the localhost address printed by Vite, then inspect Chrome DevTools Network, filtering by JavaScript and preserving the log if necessary. Before clicking, look for the entry module and confirm that report.js has not yet been requested. After clicking Load report, look for the request that supplies report.js. The code sets the status to Loading... before importing and then displays two report lines. On a fast connection, the loading message can disappear before a rendered frame makes it noticeable. If the import fails, the example displays an error rather than silently leaving the button disabled.

Next, compare this with the production output. Stop the development server with Ctrl+C before starting the preview, or use a separate terminal and the preview URL printed by Vite. Run the build before previewing it:

npx vite build
npx vite preview

The first command writes built assets to dist/ by default. Inspect the generated dist/assets/ files and the Network panel in the preview. You are no longer looking at the original source-module URL pattern; Vite bundles and optimizes modules for production and can emit separate chunks for dynamic imports. Inspect what was actually emitted rather than assuming that one import always becomes exactly one chunk. The preview command is for checking the build locally, not a production hosting server. See building for production.

For a meaningful comparison, record four different observations instead of a single "Vite is fast" impression:

ObservationWhat to inspectWhat it can tell you
Cold dev page loadRequests, transferred bytes, time until the page is usableCost of the development module graph and dependency preparation
Warm dev page loadSame page after dependencies are cachedWhether cache state explains the difference
Edit a module in this vanilla fixtureWhether Vite reloads the documentWithout an accepting HMR boundary, a full reload is expected; test state-preserving HMR separately in an integrated framework app
Production build and previewBuild output, initial request path, first report clickWhat is deployed and whether deferred code moves work to first use

Keep the browser, cache policy, dependency versions and machine conditions consistent when repeating the comparison. An ordinary warm browser reload is not the same experiment as restarting Vite with --force, which re-optimizes dependencies. These checks explain where time is spent; they do not establish a general performance ranking against another build tool.

HMR is not the same as preserving every piece of state

The request lab above has no HMR acceptance boundary. It tests on-demand import, not hot updating, and a full document reload after a source edit is expected. To test HMR, use an appropriate framework starter or add an explicit import.meta.hot.accept() boundary and then check edits in a running Vite development server. Vite's HMR API documentation explains how module acceptance and disposal work. Framework integrations such as React Fast Refresh or Vue's SFC integration can replace updated modules without a full navigation, but state preservation depends on the integration and what changed. This article has not run a browser HMR test.

That distinction is useful during debugging. In a framework project, if changing a component unexpectedly resets the page, check the browser console, the plugin that handles that file type, and whether the update could be accepted. Do not diagnose the issue by measuring only the interval between saving a file and seeing a visual change. Vite documents its HMR behavior and integrations.

The same care applies to TypeScript. Vite transforms TypeScript syntax during development but does not type-check your application as part of that transformation. Run tsc --noEmit or your project's equivalent in CI rather than assuming a successful vite build implies type safety. See Vite's TypeScript guidance.

When would you choose Vite?

Vite is a strong default when you are building a browser-focused application and want a configurable development/build layer without adopting an opinionated full-stack framework. The more important question is what else the application needs.

SituationVite's fitCheck before committing
Small SPA, internal dashboard or browser-only toolGood starting pointRouting, authentication, API hosting and deployment are separate architectural choices
React, Vue or other supported client-side appGood fit with the corresponding framework integrationPlugin compatibility and how updates behave in your setup
App that needs SSR, server routing or server-side data featuresPossible, but Vite is not by itself the application frameworkEvaluate a Vite-based framework or a full-stack framework that already provides those features
Established project with a working build pipelineMigration may or may not pay offAudit plugins, transforms, build targets, aliases, tests and deployment behavior first
Library or package published to npmVite can participate in the buildValidate output formats, external dependencies and package exports for consumers

If server rendering matters, Vite exposes lower-level SSR support, but it does not automatically supply the conventions of a complete application framework. Likewise, replacing the build tool does not by itself change the browser's rendering pipeline. For that separate concern, see how browsers turn resources into pixels.

What changed for teams upgrading to Vite 8?

Older articles often say "esbuild for development and Rollup for production." That describes earlier Vite versions, not Vite 8. Vite 8 uses Rolldown as its bundler and Oxc-based tooling. Existing projects need to review the Vite 8 migration guide, especially plugin and configuration compatibility, before assuming a major-version update is behaviorally identical.

Do not treat release-note benchmark claims as your application's own results. Run the existing build and representative tests on both versions, compare emitted assets, and validate the app in a browser. A faster build process does not automatically improve Core Web Vitals.

Make the decision against the job, not the slogan

Choose Vite when its development-server model, ecosystem and build configuration fit your application. Then check three separate outcomes: how soon the developer can use the page, how well changes are handled, and what the production build actually sends to users.

If you find a large deferred chunk in the production output, the next question is whether delaying it improves the initial experience without making the first interaction worse. That is a code-splitting decision, not a Vite-specific guarantee.

A quick dev server is a useful tool. It is not a substitute for testing the application it serves.