The Imports Object
Not every WebAssembly module is self-contained. When a module needs to call a JavaScript function, read from shared memory, or access a global value managed by the host, it declares those dependencies with (import ...) statements. At instantiation time, JavaScript must supply matching values — this is the imports object.
Import declaration syntax
Section titled “Import declaration syntax”An import declaration names the thing being imported and describes its type. The first two string arguments form a two-level namespace: a module name (the outer key) and a field name (the inner key):
(module (import "env" "log" (func $log (param i32))))Here "env" is the module namespace and "log" is the field name. The declaration says: “this module requires a function called log inside the env namespace; that function must accept one i32 argument.”
Shape of the imports object
Section titled “Shape of the imports object”The imports object passed to WebAssembly.instantiate() mirrors that two-level namespace — the outer key is the module name and the inner key is the field name:
const importObject = { env: { log: (n) => console.log(n) }};Each inner value must match the type declared in the WAT. A function import needs a JS function; a memory import needs a WebAssembly.Memory; a global import needs a WebAssembly.Global.
Importing a JS function
Section titled “Importing a JS function”Here is a complete WAT module that imports the log function and calls it from an exported run function:
(module (import "env" "log" (func $log (param i32))) (func (export "run") i32.const 42 call $log))The run function pushes the constant 42 onto the stack, then calls $log — which consumes that value and passes it to whatever JS function was provided in the imports object.
On the JavaScript side:
const { instance } = await WebAssembly.instantiate(bytes, { env: { log: (n) => console.log('Wasm says:', n) }});instance.exports.run(); // prints: Wasm says: 42Run it
Section titled “Run it”What else can be imported
Section titled “What else can be imported”Functions are the most common import, but the same mechanism works for memory, globals, and tables:
// Import a shared Memoryconst memory = new WebAssembly.Memory({ initial: 1 });const importObject = { env: { memory, log: (n) => console.log(n) }};A WebAssembly.Memory import lets JavaScript and Wasm share the same ArrayBuffer, making it possible to pass strings, arrays, and other structured data back and forth by reading and writing raw bytes. Globals and tables follow the same pattern — declare them in WAT with (import ...) and supply the matching WebAssembly.Global or WebAssembly.Table instance in the imports object.
Trade-offs
Section titled “Trade-offs”| Option | Benefit | Cost |
|---|---|---|
instantiateStreaming + imports object | Imports can be supplied as the stream compile starts, cutting latency before imported code is first called | The server must respond with Content-Type: application/wasm, or it rejects with a TypeError before imports are even checked |
instantiate on a full buffer | Ignores the server’s MIME type entirely, so it always works | Has to wait for the full download first, so imported host functions are ready later |
Common mistakes
Section titled “Common mistakes”- A field name or capitalization in the imports object that doesn’t exactly match the
(import ...)declaration — e.g.Loginstead oflog— causing aLinkError - Passing a large string or array to an imported function one argument at a time, instead of writing it into shared memory once and passing just a pointer/length
- Forgetting to use
BigIntwhen an imported function must receive ani64parameter from the Wasm side, so the JS callback receives an unexpected value
💡 Real-world example
Applications that port a large existing native engine (CAD tools, game engines, physics simulators) to run in the browser typically
importdozens of host functions — for rendering, input handling, and file I/O — to bridge the ported engine to browser APIs.