close
  • English
  • WebAssembly

    Rslib supports WebAssembly (.wasm) modules used with the WebAssembly ESM Integration proposal syntax.

    Warning

    Source code that uses the WebAssembly ESM Integration proposal syntax can only be built as ESM output, so format must be 'esm' (the default value).

    This is because compiling and instantiating a .wasm module is asynchronous. A JavaScript module that statically imports it also becomes an async module, and asynchronous evaluation propagates through the importing module graph. CommonJS, UMD, and IIFE do not provide module evaluation and export semantics equivalent to ESM async modules, so these imports cannot be transformed reliably.

    Import wasm in JavaScript file

    You can import a .wasm module using the ESM module import syntax:

    src/index.ts
    import { add } from './add.wasm';
    
    export const sum = add(1, 2);

    Rslib supports the following import forms for .wasm modules.

    Static import and export

    Access instantiated exports through standard ESM bindings:

    import { add } from './add.wasm';
    import * as wasm from './add.wasm';
    import './add.wasm';
    
    export { add } from './add.wasm';
    export * from './add.wasm';
    export * as wasm from './add.wasm';

    Dynamic import

    const wasm = await import('./add.wasm');

    Source phase import

    import source is part of WebAssembly ESM Integration, and Rspack provides first-class support for it. It imports the compiled WebAssembly.Module instead of the instantiated exports, so you can instantiate it yourself with a custom import object:

    import source addModule from './add.wasm';
    
    const { instance } = await WebAssembly.instantiate(addModule, {
      env: { now: Date.now },
    });

    Dynamic source phase import is also supported through import.source():

    const addModule = await import.source('./add.wasm');
    Note

    TypeScript does not currently parse import source or import.source(). Write source phase imports in JavaScript files, or use a toolchain that supports the syntax.

    Configure output

    Rslib provides two modes to emit .wasm modules, controlled by lib.wasm.mode.

    • Compile mode: Rspack handles .wasm imports during the build, generates JavaScript runtime code that loads and instantiates WebAssembly, and emits the binary as a static asset. Consumers do not need to support ESM Integration.
    • Preserve mode: In bundleless builds, Rslib retains .wasm ESM Integration imports in the JavaScript output and emits the binary as-is. The downstream build tool or runtime is responsible for resolving and loading the .wasm module.

    The default value depends on bundle:

    bundleDefault mode
    truecompile
    falsepreserve

    If the output needs to be consumed directly by a JavaScript runtime such as a browser, use Compile mode. If a bundleless output will be processed by a build tool that supports ESM Integration, such as Rsbuild, or if the target runtime (such as Node.js ^22.19.0 || >=24.5.0) supports this feature natively, you can use Preserve mode. Rslib rejects bundle: true together with wasm.mode: 'preserve'; use Compile mode for bundled output.

    Compile mode

    In Compile mode, Rspack parses the .wasm module and generates JavaScript glue code that loads and instantiates it at runtime.

    The binary is emitted as an asset with a content-hashed filename under output.distPath.wasm (defaults to static/wasm):

    bundle
    bundleless
    dist
    ├── index.js
    └── static
        └── wasm
            └── [contenthash].module.wasm

    The generated loading runtime is derived from output.target:

    • For web targets, .wasm is loaded with fetch.
    • For node targets, .wasm is loaded from the file system with async Node.js APIs.

    Preserve mode

    Preserve mode requires bundle to be false. Rslib does not generate any WebAssembly loading runtime. It keeps a real .wasm import in the JavaScript output and emits the binary as-is.

    The .wasm file is copied to the output using the same source-relative path and original filename:

    dist
    ├── index.js
    └── add.wasm
    dist/index.js
    import { add } from './add.wasm';
    
    export { add };

    The .wasm filename in Preserve mode is not affected by output.filenameHash.

    Rslib updates JavaScript imports to follow the emitted .wasm files, including when output.filename.js places JavaScript in another directory. However, the WebAssembly binary is emitted without modification. If it imports JavaScript, those imports still use their original relative paths and filenames. In this case, do not move or rename the imported JavaScript files in the output. For example, changing index.js to js/index.js or index.mjs would break a WebAssembly import of ./index.js.

    Use wasm-bindgen

    wasm-bindgen is a tool for building WebAssembly libraries with Rust. The --target option generates output for different runtime environments.

    Rslib supports the following targets:

    bundler

    The JavaScript glue imports the .wasm file as an ES module, and the .wasm module imports back into the glue for its imports object.

    Because of this two-way relative dependency, Preserve mode requires the output to keep the relative path and filename between the .wasm file and its JavaScript glue. In particular, avoid moving the glue with output.filename.js or changing its extension with autoExtension.

    bundlewasm.modeSupported
    truecompile
    falsecompile
    falsepreserve✅ When the relative path and filename of the glue remain unchanged
    truepreserve❌ Rslib rejects this configuration combination

    module

    The JavaScript glue imports the .wasm file with the source phase import syntax, and constructs the imports object entirely in JavaScript. Use Compile mode for bundled output, or Preserve mode for bundleless output.

    web / experimental-nodejs-module

    The JavaScript glue loads the .wasm file through new URL('./pkg.wasm', import.meta.url). Rslib treats it as a static asset instead of a WebAssembly ESM module.

    wasm.mode does not apply in this case.

    Type declaration

    TypeScript does not provide built-in module declarations for .wasm files. If you use TypeScript, add a declaration file next to the .wasm file using the .d.wasm.ts extension, and enable allowArbitraryExtensions in tsconfig.json:

    src/add.d.wasm.ts
    export function add(a: number, b: number): number;