---
title: "Vite 8.1.0 Stable Adds Bundled Dev Mode (15x Startup on 10k React Components), Ships WASM ESM Imports and Chunk Import Map, Extends server.fs.deny With .npmrc and Private Keys"
description: "Vite 8.1.0 stable, published 2026-06-23 by the Vite Team (announcing-vite8-1), graduates Bundled Dev Mode from an experimental flag into a documented --experimental-bundle entry point (15x faster cold start on a 10k React-component app, 10x faster full reloads, 3x faster cold-start rendering and 40% faster full reloads reported by Linear), stabilises the WASM ESM Integration direct .wasm imports and build.chunkImportMap that shipped in 8.1-beta on 2026-06-15, brings Lightning CSS one step closer to default by adding external-CSS-in-CSS and plugin file dependencies, bumps Rolldown to 1.1.2 (1.1.3 follows the same day), pins rolldown with a tilde range so patch releases flow through without a Vite PR, extends server.fs.deny with .npmrc, .yarnrc.yml, and *.{key,p12,pfx,cer,der}, and emits a runtime warning when envFile: false is used (the deprecation is already documented in the types)."
date: 2026-06-24
image: "/images/heroes/2026-06-24--vite-8-1-stable-bundled-dev-mode.png"
author: lschvn
tags: ["tooling", "performance"]
tldr:
  - "Vite 8.1.0 stable ([announcing-vite8-1](https://vite.dev/blog/announcing-vite8-1)) shipped 2026-06-23 from the same [8.1-beta.0 branch](/articles/2026-06-15--vite-8-1-beta-wasm-esm-chunk-importmap) that landed on 2026-06-15. It graduates Bundled Dev Mode into a documented experimental entry point: `--experimental-bundle` on the CLI or `experimental.bundledDev: true` in vite.config.js. The Vite Team measured 15x faster cold start on a 10,000-component React app and 10x faster full reloads while keeping HMR instant; Linear reported 3x faster cold start rendering, ~40% faster full reloads, and 10x fewer network requests on their app."
  - "The release stabilises the two big beta features: WASM ESM Integration direct .wasm imports (no `?init` suffix) and `build.chunkImportMap`, which uses browser import maps to keep chunk hashes stable when only a single source file changes. It also adds `import.meta.glob` `caseSensitive`, `html.additionalAssetSources` for custom elements, Lightning CSS plugin file dependencies and external-CSS-in-CSS support (one step closer to default), and renames `server.hmr` options to `server.ws` (the only breaking change in the line)."
  - "Other concrete changes: `server.fs.deny` extends its default from `['.env', '.env.*', '*.{crt,pem}', '**/.git/**']` to also include `.npmrc`, `.yarnrc.yml`, and `*.{key,p12,pfx,cer,der}` (PR #22707); rolldown is pinned with a tilde range (`~1.1.1`) so 1.1.2 and 1.1.3 flow through without a Vite PR (PR #22693); envFile: false now emits a runtime deprecation warning (PR #22555); and Rolldown 1.1.3 ships the same day fixing a `defer_drop` browser-main-thread crash and a watch-mode close reentrancy issue."
faq:
  - question: "What is the headline feature of Vite 8.1.0 stable?"
    answer: "Bundled Dev Mode is the headline. It is no longer behind a flag with no docs: 8.1 documents it as `--experimental-bundle` on the CLI or `experimental.bundledDev: true` in vite.config.js, and explains the tradeoff in a dedicated design document (vitejs/vite#22746). The dev server now pre-bundles the application for the browser target and serves the prebuilt chunks, instead of streaming the unbundled module graph the way Vite has since v1. The Vite Team measured 15x faster cold start on a 10,000-component React app, 10x faster full reloads, and instant HMR regardless of app size; Linear reported 3x faster cold start rendering, ~40% faster full reloads, and 10x fewer network requests in production-scale tests. The other two stable features are direct .wasm imports via WASM ESM Integration and `build.chunkImportMap` for stable chunk caching."
  - question: "How does Bundled Dev Mode differ from the default Vite dev server?"
    answer: "The default Vite dev server is unbundled: it serves each source module as its own ESM file and lets the browser resolve the import graph. That is what made Vite fast at small scale, but as apps grew past a few thousand modules the per-module request overhead dominated cold start and full reload. Bundled Dev Mode keeps the same dev-server ergonomics (HMR, the plugin pipeline, env vars) but pre-bundles the application with Rolldown and serves the prebuilt chunks. The tradeoffs: third-party plugins that rely on per-module transforms may need updates, and the feature focuses on browser-side plugins and core features for now. It is still experimental because the plugin surface is being shaped; see vitejs/vite#22746 for the roadmap."
  - question: "What changes between Vite 8.1-beta.0 and 8.1.0 stable?"
    answer: "Most of the work happened in the beta. The stable release adds three concrete features, fixes, and a structural pin. The features: `server.fs.deny` is extended with `.npmrc`, `.yarnrc.yml`, and certificate/key extensions (`key, p12, pfx, cer, der`) for a less surprising default secure-by-default dev server (PR #22707). The fixes: a malformed-URI crash in the memory-files middleware (PR #22714), a perEnvironmentState cache that lost falsy values (PR #22715), a glob hmr matcher that ignored the caseSensitive option (PR #22711), and a `bundled-dev` incremental-build error that was dropped on the floor instead of surfaced (PR #22617). The structural change: rolldown is now pinned with a tilde range (`~1.1.1`) instead of an exact version, so patch releases like 1.1.2 (which Vite 8.1 ships) and 1.1.3 (which shipped the same day) flow into Vite without a per-release Vite PR."
  - question: "Are direct .wasm imports and build.chunkImportMap ready for production use?"
    answer: "Yes, both graduated from the 8.1 beta and are now stable in 8.1.0. Direct .wasm imports use the WebAssembly community group's WASM ESM Integration draft, so the import path matches the future browser-level spec; Vite still does the binary parsing and glue generation in the meantime. `build.chunkImportMap` is implemented on top of Rolldown's experimental chunkImportMap feature and depends on `import.meta.resolve` in the browser, so it does not apply to older browsers; the companion plugin-legacy release covers them with SystemJS. The known caveat is that `experimental.renderBuiltUrl` does not currently work with chunkImportMap, and the optimisation does not yet apply to CSS and assets, only to JavaScript chunks."
  - question: "What does the server.fs.deny extension actually cover?"
    answer: "The default `server.fs.deny` list goes from `['.env', '.env.*', '*.{crt,pem}', '**/.git/**']` (Vite 8.0.x) to `['.env', '.env.*', '*.{crt,pem,key,p12,pfx,cer,der}', '.npmrc', '.yarnrc.yml', '**/.git/**']` (Vite 8.1.0). The new entries cover private-key and certificate material in `.key`, `.p12`, `.pfx`, `.cer`, and `.der` files (the same family as the existing `.crt` and `.pem` coverage) plus the package-manager credential files `.npmrc` and `.yarnrc.yml`. The PR description explicitly notes this is not framed as a vulnerability fix; it is a hardening of the default list so that a fresh project without a custom `server.fs.deny` does not accidentally serve those files through the dev server. The list is documented as the override surface; projects with stricter needs can still set `server.fs.deny` explicitly."
  - question: "Does Vite 8.1 have any breaking changes?"
    answer: "One: the `server.hmr` to `server.ws` rename that landed in 8.1-beta.0. Every WebSocket-related option (host, port, clientPort, path, timeout, overlay) moves from `server.hmr` to `server.ws`, and `server.hmr` becomes a boolean toggle. The migration is mechanical: split the old `server.hmr: { host, port, ... }` block into `server.ws: { host, port, ... }` plus a boolean `server.hmr` if you need it. There is also a soft runtime deprecation: passing `envFile: false` now emits a warning (PR #22555), though the backward-compatible behavior that maps `envFile: false` to `envDir: false` is preserved."
  - question: "What is in Rolldown 1.1.2 and 1.1.3?"
    answer: "Rolldown 1.1.2 (2026-06-18) bumps oxc_resolver to 11.21.3, which makes `compilerOptions.paths` resolve for importers whose extension is explicitly listed in a tsconfig's `include` (e.g. `src/**/*.vue`, `src/**/*.svelte`). This unblocks the default create-vite Vue + TS layout that has a solution-style root plus a referenced tsconfig.app.json that declares `paths` and `include`. Rolldown 1.1.3 (2026-06-24, same day as this article) ships ten fixes: a `defer_drop` crash on the browser main thread (#9942), a camelCase bug for nested values in config (#9933), a `--help` display bug (#9941), a preserveModules re-export bug (#9934), a watch-mode close-reentrancy fix (#9904), a Git-for-Windows symlink-as-regular-file fix (#9915), cancellation of pending full reloads on build errors (#9903), passing plugin meta to the codeSplitting groups name function (#9267), serving assets emitted during HMR and lazy compile (#9815), and a dry-run release that no longer publishes binding packages (#9866)."
  - question: "What is the upgrade path from Vite 8.0?"
    answer: "Vite 8.1.0 is a non-breaking release for projects that have already migrated their `server.hmr` config to `server.ws`. If you are still on the 8.0.x line, the path is `npm install -D vite@8.1.0` (or `bun add -D vite@8.1.0`) and then a one-time config edit if your project sets any `server.hmr` WebSocket options. The release also ships a security-relevant default change: `server.fs.deny` now blocks `.npmrc`, `.yarnrc.yml`, and a wider set of certificate/key files, so projects that previously served those files through the dev server (an unusual pattern, but possible for credential-loading demos) will need to add them to `server.fs.allow` explicitly."
---

[Vite 8.1.0 stable](https://github.com/vitejs/vite/releases/tag/v8.1.0), published 2026-06-23 by github-actions from the v8.1.0 tag ([commit 63b1489](https://github.com/vitejs/vite/commit/63b1489), `release: v8.1.0`), is the first feature release on the [Vite 8 stable branch](/articles/2026-04-08--vite-8-stable-seven-patches-in-three-weeks) since the 8.1-beta on 2026-06-15. The release ships with the same code line that produced the beta, plus one new documented experimental entry point (Bundled Dev Mode), three weeks of bug-fix patches (the [beta CHANGELOG](https://github.com/vitejs/vite/blob/v8.1.0/packages/vite/CHANGELOG.md) lists 22 commits between `v8.1.0-beta.0` and `v8.1.0`), a tightened default `server.fs.deny` list, a tilde-pinned Rolldown dependency that lets the bundler ship patch releases without a Vite PR, and a runtime deprecation warning for `envFile: false`. The same day, Rolldown 1.1.3 ships with ten follow-up fixes including a browser-main-thread crash and a watch-mode close-reentrancy fix.

The release lands eight days after the beta, which is faster than the [Vite 7 → Vite 8](/articles/2026-04-08--vite-8-stable-seven-patches-in-three-weeks) cadence. The Vite team's [announcement post](https://vite.dev/blog/announcing-vite8-1) notes that Vite is now seeing 41.6 million weekly downloads on npm, "almost reaching the total downloads of Vite 7." Bundled Dev Mode is the feature that justifies the new minor line on its own: a 15x cold-start improvement on a 10,000-component React app, and Linear's real-world 3x faster cold start + 10x fewer network requests.

## Bundled Dev Mode, the headline

The single biggest addition in 8.1.0 stable is [Bundled Dev Mode](https://github.com/vitejs/vite/discussions/22746), which is no longer a flag with no docs. It now has a dedicated CLI entry point, `--experimental-bundle`, and a documented config option, `experimental.bundledDev: true`:

```ts [vite.config.js]
import { defineConfig } from 'vite'

export default defineConfig({
  experimental: {
    bundledDev: true,
  },
})
```

The default Vite dev server is unbundled: each source module is served as its own ESM file, and the browser resolves the import graph. That is what made Vite fast at small scale, but as apps grew past a few thousand modules the per-module request overhead dominated cold start and full reload. Bundled Dev Mode keeps the same dev-server ergonomics (HMR, the plugin pipeline, env vars) but pre-bundles the application with Rolldown and serves the prebuilt chunks. The Vite Team measured 15x faster cold start on a 10,000-component React app, 10x faster full reloads, and instant HMR regardless of app size; the [Linear](https://linear.app) team reported 3x faster cold start rendering, ~40% faster full reloads, and 10x fewer network requests on their app.

The feature is still experimental because the plugin surface is being shaped. The release notes call out that "if you are using a third party plugin, it may not work with this mode" and that the focus is on browser-side core features for now; the [design document](https://github.com/vitejs/vite/discussions/22746) lays out the planned plugin contract. Two follow-ups are already merged into the next 8.x line: [PR #22587](https://github.com/vitejs/vite/issues/22587) folded `bundledDev` into `DevEnvironment` instead of being a separate subclass (it shipped in 8.1-beta.0), and [PR #22617](https://github.com/vitejs/vite/issues/22617) makes sure incremental build errors are surfaced rather than dropped on the floor in `bundled-dev` mode.

## WASM ESM Integration and Chunk Import Map graduate

The two big beta features graduate unchanged from the [8.1-beta.0 branch](/articles/2026-06-15--vite-8-1-beta-wasm-esm-chunk-importmap): [PR #21779](https://github.com/vitejs/vite/issues/21779) implements the [WASM ESM Integration](https://github.com/WebAssembly/esm-integration) draft from the WebAssembly community group as direct `.wasm` imports with no `?init` suffix, and [PR #21580](https://github.com/vitejs/vite/issues/21580) adds `build.chunkImportMap`, an opt-in build option that uses browser import maps to keep chunk hashes stable when only a single source file changes.

The WASM feature replaces the old `import init from './add.wasm?init'; const instance = await init();` pattern with `import { add } from './add.wasm'`. Vite parses the binary, extracts imports and exports, and emits glue code that returns a properly-typed `WebAssembly.Module` instance. The legacy `?init`, `?url`, and `?raw` query suffixes still work, so existing projects do not need to migrate in lockstep. The feature is independent of browser support today, because Vite still does the parsing and glue generation; the browser-level spec just standardizes the long-term target.

The chunk import map feature matters at a different scale: it is a real cache-stability improvement for production builds, not a dev-server convenience. In a default Rolldown build, every chunk filename contains a content hash, and import statements point directly at the hashed URL. When a single source file changes, every chunk that imports it (directly or transitively) gets a new hash, which cascades through the import graph and invalidates more chunks than strictly necessary. Import maps decouple the import statement from the chunk URL: the statement says `import { x } from '/chunks/x.js'`, the import map says `/chunks/x.js` resolves to `/chunks/x-abc123.js`, and when the chunk content is unchanged the hashed URL stays the same and the browser reuses it. The implementation relies on `import.meta.resolve` in the browser, so `chunkImportMap` only works on browsers that support it; the companion plugin-legacy covers older browsers with SystemJS.

## Lightning CSS, one step closer to default

[PR #21748](https://github.com/vitejs/vite/issues/21748) makes Vite honor plugin dependencies declared by Lightning CSS itself, and [PR #18389](https://github.com/vitejs/vite/issues/18389) adds support for external CSS files imported inside CSS files. Both pieces were already shipping as Lightning CSS issues ([lightningcss#479](https://github.com/parcel-bundler/lightningcss/issues/479) and [lightningcss#877](https://github.com/parcel-bundler/lightningcss/issues/877)) and are now wired into Vite. The release notes say the team is "thinking of changing the default CSS preprocessor to Lightning CSS in the next major release." To try it now, set `css.transformer: 'lightningcss'` in vite.config.js and share feedback in [vitejs/vite#13835](https://github.com/vitejs/vite/discussions/13835).

## `server.fs.deny` extension

[PR #22707](https://github.com/vitejs/vite/issues/22707) (sapphi.red, feat: extend `server.fs.deny` list with common files) tightens the default secure-by-default dev server. The deny list goes from `['.env', '.env.*', '*.{crt,pem}', '**/.git/**']` (Vite 8.0.x) to `['.env', '.env.*', '*.{crt,pem,key,p12,pfx,cer,der}', '.npmrc', '.yarnrc.yml', '**/.git/**']` (Vite 8.1.0). The new entries cover private-key and certificate material in `.key`, `.p12`, `.pfx`, `.cer`, and `.der` files (the same family as the existing `.crt` and `.pem` coverage) plus the package-manager credential files `.npmrc` and `.yarnrc.yml`.

The PR description is explicit that this is not framed as a vulnerability fix: "we do not consider this as a vulnerability as it's not possible to cover every sensitive files possible. But we can add common ones as we go. And the `server.fs.deny` list is documented, so if you need more than these, you should add as you see fit." In practice this is the kind of change that prevents accidental credential leaks when a developer serves a project's root directory without a custom `server.fs.allow` configuration, which is the default for new projects. The behavior is the same in dev and SSR mode.

## Tilde-pinned Rolldown

[PR #22693](https://github.com/vitejs/vite/issues/22693) (use `~` for Rolldown) is small but worth flagging for ecosystem users. Vite's package.json previously pinned rolldown as `"rolldown": "1.1.1"` (exact version); 8.1.0 pins it as `"rolldown": "~1.1.1"` (tilde range, accepts any 1.1.x). The motivation is operational: Rolldown ships patch releases on a faster cadence than Vite, and Vite was having to bump rolldown manually for every patch release. With the tilde range, 1.1.2 (2026-06-18) and 1.1.3 (2026-06-24) flow into Vite users via the lockfile without a Vite PR each time.

The risk is the tilde-range version drift: a Rolldown patch that lands in 1.1.x and changes behavior could land in a Vite user's install without an explicit Vite release. The mitigation is that patch semver is meant to be backwards-compatible, and the Vite team's release cadence is fast enough that they can ship a Vite patch within a day or two if a bad rolldown patch slips through. The 1.1.3 release the same day as this article is the new rhythm: a Rolldown bug fix can be in Vite users' installs within hours of being merged.

## `envFile: false` deprecation warning

[PR #22555](https://github.com/vitejs/vite/issues/22555) adds a runtime deprecation warning when `envFile: false` is used. The behavior does not change; the existing backward-compatible mapping of `envFile: false` to `envDir: false` is preserved. The motivation is that `envFile` was already marked deprecated in the type definition, but the runtime path stayed silent, which made migration harder for programmatic API users and left behavior inconsistent with other deprecated Vite options that warn when used. The fix is a one-line warning plus the matching config tests; users who already use `envDir: false` get no warning.

## Other changes between beta and stable

The 22-commit [CHANGELOG diff](https://github.com/vitejs/vite/blob/v8.1.0/packages/vite/CHANGELOG.md) between `v8.1.0-beta.0` and `v8.1.0` is mostly bug fixes plus the Rolldown 1.1.2 bump:

- [PR #22714](https://github.com/vitejs/vite/issues/22714) handles malformed URIs in the memory-files middleware.
- [PR #22715](https://github.com/vitejs/vite/issues/22715) caches falsy values in `perEnvironmentState`, which is a small correctness fix for environments that previously re-evaluated expensive work on every request.
- [PR #22711](https://github.com/vitejs/vite/issues/22711) makes the glob hmr matcher respect the `caseSensitive` option.
- [PR #22713](https://github.com/vitejs/vite/issues/22713) omits the `nonce` attribute on import maps when `cspNonce` is unset (a small HTML correctness fix).
- [PR #22611](https://github.com/vitejs/vite/issues/22611) skips null-valued exports in `expandGlobIds` glob resolution.
- [PR #22691](https://github.com/vitejs/vite/issues/22691) keeps resolved build options as a getter so plugins that introspect `config.build` see the resolved values.
- [PR #22706](https://github.com/vitejs/vite/issues/22706) uses literal envPrefix queries for Vite Task.
- [PR #22736](https://github.com/vitejs/vite/issues/22736) inlines the dev-id value in the CSS selector (a small client-side size optimisation).
- [PR #22724](https://github.com/vitejs/vite/issues/22724) removes the unused `removeRawQuery` utility.
- [PR #22692](https://github.com/vitejs/vite/issues/22692) renames `chunkImportMap` related options to use the `rolldownOptions` property.
- [PR #22695](https://github.com/vitejs/vite/issues/22695) bumps Rolldown to 1.1.2.

## Why the timing matters

The Vite team published the [announcing-vite8-1 post](https://github.com/vitejs/vite/blob/v8.1.0/docs/blog/announcing-vite8-1.md) on the same day as the release, which is a faster-than-usual turnaround from "beta" to "stable + announcement" (eight days for 8.1 vs roughly three weeks for the 8.0 line). The 41.6M weekly downloads stat is a reminder that Vite is now in the same install-base tier as React, and the Bundled Dev Mode metrics are the kind of headline numbers that justify a minor line bump on their own.

For users on the [Vite 8 stable line](/articles/2026-04-08--vite-8-stable-seven-patches-in-three-weeks), the upgrade is `npm install -D vite@8.1.0` plus a one-time config edit if your project sets any `server.hmr` WebSocket options. For users still on Vite 7 or earlier, the [Vite 8 stable release notes](/articles/2026-04-08--vite-8-stable-seven-patches-in-three-weeks) cover the Rolldown migration and the larger plugin surface changes. The 8.1 line is the recommended stable target for new projects today, with 8.2 and 9.0 already open as milestones on the Vite repo.
