Vite’s default handling of WebAssembly modules is surprisingly permissive, often treating them like plain JavaScript imports without explicit configuration.

Let’s see what happens when we try to import a .wasm file directly in a Vite project.

First, create a simple WebAssembly module. Save this C code as add.c:

int add(int a, int b) {
    return a + b;
}

Now, compile it to WebAssembly using Emscripten:

emcc add.c -o add.wasm -s WASM=1 -s SIDE_MODULE=1

This command compiles add.c into add.wasm. The -s SIDE_MODULE=1 flag is crucial here; it tells Emscripten to produce a standalone WebAssembly module that can be loaded by JavaScript, rather than a full HTML/JS application.

Next, set up a basic Vite project. If you don’t have one, create it with:

npm create vite@latest my-wasm-app --template react
cd my-wasm-app
npm install

Now, place the generated add.wasm file into the public/ directory of your Vite project. This is important because Vite, by default, doesn’t process files in public/ as modules.

In your App.jsx (or equivalent entry point), import and use the WebAssembly module:

import React, { useEffect, useState } from 'react';
import addWasmUrl from './add.wasm'; // Vite will resolve this path

function App() {
  const [result, setResult] = useState(null);

  useEffect(() => {
    const loadWasm = async () => {
      try {
        // Vite's default asset handling will give us a URL
        const response = await fetch(addWasmUrl);
        const bytes = await response.arrayBuffer();
        const module = await WebAssembly.instantiate(bytes, {});
        const { add } = module.instance.exports;
        const sum = add(5, 10);
        setResult(sum);
      } catch (error) {
        console.error("Error loading or running Wasm:", error);
        setResult("Error");
      }
    };
    loadWasm();
  }, []);

  return (
    <div>
      <h1>WebAssembly Integration</h1>
      <p>5 + 10 = {result !== null ? result : 'Loading...'}</p>
    </div>
  );
}

export default App;

When you run npm run dev, Vite will process the add.wasm import. By default, Vite treats .wasm files as assets. It will copy the add.wasm file to your build output directory and provide a URL to it. The fetch call then retrieves this URL, loads the WebAssembly bytes, and instantiates it using the WebAssembly JavaScript API.

The problem Vite solves here is asset handling. Instead of manually copying add.wasm to your build output or managing its URL, Vite automatically takes care of it when you import it like a module. It essentially turns the .wasm file into a URL asset that your JavaScript can fetch.

The mental model to build is that Vite acts as a sophisticated asset pipeline. When you import a .wasm file, Vite intercepts this import. It doesn’t try to compile or bundle the WebAssembly bytecode itself. Instead, it registers the .wasm file as a generic asset. During the build process, it copies this asset to the output directory and generates a unique URL for it. Your JavaScript then uses this URL to fetch the raw .wasm binary data, which is then passed to the native WebAssembly.instantiate API.

The levers you control are primarily around how Vite handles assets. For example, you can configure Vite to use different asset types for different file extensions. For WebAssembly, the default behavior of treating it as an asset that gets a URL is usually what you want. You could, however, create a custom plugin if you needed to transform the .wasm file before it’s served, though this is uncommon for raw WebAssembly modules.

What most people don’t realize is that Vite’s built-in support for .wasm files is actually a form of "asset optimization." It’s not about bundling Wasm into your JS, but about making the Wasm file accessible as a separate asset with a predictable URL, leveraging the browser’s native Wasm runtime. This is why you still need the fetch and WebAssembly.instantiate calls in your JavaScript – Vite is just providing the URL to the file.

The next concept you’ll likely explore is optimizing WebAssembly module loading and instantiation, perhaps by using techniques like streaming compilation.

Want structured learning?

Take the full Vite course →