Vite 8.1 Beta Lands Direct `.wasm` Imports, `build.chunkImportMap`, and a `server.hmr` → `server.ws` Rename

Vite 8.1 Beta Lands Direct `.wasm` Imports, `build.chunkImportMap`, and a `server.hmr` → `server.ws` Rename

lschvn

Vite 8.1.0-beta.0 shipped on June 15, 2026, the first feature release on the Vite 8 stable branch and the first 8.x beta since the Vite 8 stable cut on March 12. After ten weeks of small patch releases against the v8.0 line, the Vite team is using 8.1 to land a cluster of changes that have been sitting in PRs for months: a real implementation of the WASM ESM Integration standard for direct .wasm imports, a build.chunkImportMap option that uses browser import maps to stabilize chunk caching, integration with the Voidzero Vite Task runner for zero-config build caching, support for lightningcss plugin dependencies, and a long-overdue breaking rename that moves every server.hmr option to server.ws. The release also bumps Rolldown to 1.1.1 (see the Rolldown 1.1.0 notes for the 1.1 line background).

The beta drops just over three months after Vite 8.0 stable, which is a faster cadence than the 5.x to 6.x transition. Most of the 8.1 features are opt-in or behind new config keys, so the upgrade from 8.0 is largely non-breaking, with the one notable exception of the server.hmr rename, which will require a config edit for any project that currently sets WebSocket options.

Direct .wasm imports via the WASM ESM Integration standard

The headline feature of 8.1 is PR #21779, which closes the long-standing issue #4551 by adding support for importing .wasm files directly without the ?init suffix. The feature implements the WASM ESM Integration draft from the WebAssembly community group, the same spec that lets browsers treat .wasm files as ES modules natively.

The new import pattern looks like this:

// Old (Vite 8.0 and earlier)
import init from './add.wasm?init';
const instance = await init();

// New (Vite 8.1+)
import { add } from './add.wasm';
console.log(add(2, 3));

Under the hood, Vite parses the binary, extracts its imports and exports, and emits glue code that returns a properly-typed WebAssembly.Module instance. The plugin handles both dev and SSR build modes, and the ?init, ?url, and ?raw query suffixes still work, so existing code does not need to migrate in lockstep.

The shift matters because every other bundler in the JavaScript ecosystem has had to invent a non-standard import suffix for WASM. Vite 8.1 aligns the import path with the eventual browser-level spec, which means the same import statement will work in a future where browsers ship WASM ESM natively and Vite falls away. 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.

build.chunkImportMap for stable chunk caching

PR #21580 adds a new build.chunkImportMap option, implemented on top of Rolldown's experimental chunkImportMap feature. The motivation is chunk cache stability across deploys.

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 release covers older browsers with SystemJS-based import map support. Two follow-up caveats are worth noting: experimental.renderBuiltUrl does not currently work with this option, and the optimization does not yet apply to CSS and assets, only to JavaScript chunks.

The fix targets the long-running issues #6773 and #10636, which means this is a feature the Vite team has been considering since the v3 era. It is opt-in and gated by Rolldown's experimental flag, so it is worth measuring on a real production build before turning it on by default.

Vite Task integration for zero-config build caching

PR #22453 integrates Vite with the Voidzero Vite Task runner (vp run in Vite+) so that vite build can be cached with zero user configuration. The mechanism is a small new dependency, @voidzero-dev/vite-task-client, that Vite calls at the relevant code paths to declare its build inputs, outputs, and envPrefix envs. The calls are no-ops when Vite runs outside Vite Task, so there is no cost for users who are not on the runner.

The problem this solves is real and annoying: Vite Task tracks file reads and writes at the syscall level, but env vars have to be declared manually in the task config. Most projects use the VITE_* prefix convention for client-visible env vars, and previously the developer had to keep two configs in sync, the envPrefix in vite.config.ts and the env list in the Vite Task config, or the cache would silently produce wrong bundles. With 8.1, Vite reports the VITE_* envs it actually reads, and Vite Task fingerprints them automatically. Forgetting to declare an env no longer produces a stale-cache bug.

The integration is a building block in the Vite+ unified toolchain story: it makes the runner's caching story meaningful for Vite users without forcing a config migration, and it gives Voidzero a way to incrementally add more task-level optimizations to vite build without changing the Vite config surface.

The server.hmrserver.ws rename

PR #21357 is the breaking change of the release. Before 8.1, every WebSocket-related option (host, port, clientPort, path, timeout, overlay) lived under server.hmr in vite.config.ts. The problem this caused is that you could not configure the WebSocket settings when HMR itself was disabled by server.hmr: false, because the config object was off entirely.

The 8.1 split is straightforward:

// Old (Vite 8.0 and earlier)
export default defineConfig({
  server: {
    hmr: {
      host: 'localhost',
      port: 24678,
      clientPort: 24678,
    },
  },
});

// New (Vite 8.1+)
export default defineConfig({
  server: {
    hmr: false, // boolean toggle, same as before
    ws: {
      host: 'localhost',
      port: 24678,
      clientPort: 24678,
    },
  },
});

The migration is mechanical: grep your config for server.hmr and split it into server.ws (for the WebSocket options) and server.hmr (for the boolean enable flag). Any config that only used server.hmr: true or server.hmr: false does not need changes. The rename has been discussed since issue #18489 was opened, and it lands in 8.1 as the single breaking change of the line.

Other 8.1 changes worth knowing

The rest of the 8.1 beta is a mix of small ergonomic improvements and refactors:

  • html.additionalAssetSources (#21412) lets you register custom HTML elements and attributes as asset sources, for things like <html-import src="..."> or <img data-src-dark="..." data-src-light="...">. Without this, Vite only rewrites URLs in a hardcoded list of elements, which breaks the URLs in custom web components.
  • import.meta.glob caseSensitive option (#21707) gives glob pattern matching an opt-in case-sensitive mode. The default remains case-insensitive to match glob semantics, but on case-sensitive filesystems (Linux, macOS) the case-insensitive match can produce surprising results.
  • Lightningcss plugin dependency support (#21748) makes Vite honor plugin dependencies declared by lightningcss itself, so adding a lightningcss plugin no longer requires manual registration on the Vite side.
  • Multiple hosts in __VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS (#21501) lets the env variable take a comma-separated list of allowed hosts, instead of a single one.
  • Dependency tracking for native config loading (#22602) tracks dependencies of the vite.config.ts when Vite loads it through the native Node loader, so edits to imported config files correctly trigger a dev server restart.
  • bundledDev folded into DevEnvironment (#22587) removes the separate DevEnvironment subclass for bundledDev mode. The flag is now an option on the standard DevEnvironment class, which simplifies plugin code that has to handle both modes.
  • Pnpm global virtual store fs restrictions (#22415) apply the correct fs restrictions when a dep is installed in pnpm's gvs layout, so Vite correctly distinguishes which deps can be read from the dev server and which cannot.
  • Sourcemap preservation for optimized deps with follow-up transforms (#22428) keeps the original sourcemap intact when a dep is re-transformed after pre-bundling, so stack traces still point at the original source.

How to try it

bun add -D vite@8.1.0-beta.0
# or
npm install -D vite@8.1.0-beta.0

A few things to validate as you upgrade:

  1. The server.hmr rename. If your config sets any WebSocket option, move it to server.ws and keep server.hmr as the boolean enable flag.
  2. Your WASM imports. Run the dev server and a production build, and confirm that both the new direct import pattern and the legacy ?init pattern still emit working code.
  3. Plugin behavior on Rolldown 1.1.1. The Vite 8.0 stable release surfaced several plugin edge cases during the patch cycle, and 8.1 carries the same inheritance. The Rolldown update itself is small, but the combined effect on plugin chains warrants a smoke test.
  4. import.meta.glob with caseSensitive: true on case-sensitive filesystems, if your project relies on glob matching for routing or asset collection.

Vite 8.1 is a beta, not a stable release, and the Vite team will run a few weeks of beta feedback before cutting 8.1.0 stable. The breaking change is small, the new features are opt-in, and the dependency on Rolldown 1.1.1 is the same as Vite 8.0.x already had, so the upgrade risk for projects on 8.0 stable is low. Pin to 8.0.16 for production, and use 8.1.0-beta.0 to validate that your plugin chain and config survive the rename. Stable 8.1 will land in the coming weeks.

Frequently Asked Questions

Related articles

More coverage with overlapping topics and tags.

Turborepo v2.9.16 Lands Heap Allocation Profiling and pnpm Fixes
tooling

Turborepo v2.9.16 Lands Heap Allocation Profiling and pnpm Fixes

Turborepo's latest stable releases add heap allocation profiling via OpenTelemetry traces, fix pnpm injected peer package handling, harden OTEL endpoint validation, and address PTY shutdown hangs and npm tlog publish retries.
TypeScript 6.0: The Last JS Release Before the Go-Based Native Compiler
tooling

TypeScript 6.0: The Last JS Release Before the Go-Based Native Compiler

TypeScript 6.0 lands as a bridge release with new features like #/ subpath imports, stable type ordering, and a path toward TypeScript 7.0's native Go codebase.
ESLint v10 Drops Legacy Config, And the JS Ecosystem Is Taking Notes
tooling

ESLint v10 Drops Legacy Config, And the JS Ecosystem Is Taking Notes

ESLint's biggest breaking-change release in years finalises flat config, removes eslintrc support entirely, and adds JSX reference tracking. But the bigger story might be what's nipping at its heels.

Comments

Log in Log in to join the conversation.

No comments yet. Be the first to share your thoughts.