WebAssembly module boundaries
A WebAssembly module communicates with its host through two flat lists: imports
it requires and exports it offers. Both reference one of four external kinds —
func, table, memory or global. This reference shows the WebAssembly text
(WAT) syntax for each and the matching JavaScript binding used by
WebAssembly.instantiate.
How it works
Every import carries a two-level name: a module string and a field string. At
instantiation the host supplies an import object — a nested record — and the
linker matches (import "mod" "name" ...) to importObject.mod.name:
(module
(import "env" "log" (func $log (param i32)))
(import "env" "mem" (memory 1))
(global $g (export "counter") (mut i32) (i32.const 0))
(func (export "run") ... ))
const importObject = {
env: {
log: (n) => console.log(n),
mem: new WebAssembly.Memory({ initial: 1 }),
},
};
const { instance } = await WebAssembly.instantiate(bytes, importObject);
instance.exports.run();
Exports appear on instance.exports after instantiation. The supplied import
value must match the declared kind and limits, or a LinkError is thrown.
Each external kind in detail
func imports and exports
A function import names the expected type signature. The JavaScript side provides a plain function — any JS callable works. At the Wasm boundary, types are coerced: an i32 parameter becomes a 32-bit integer (truncated if needed), an f64 becomes a double, and an i64 becomes a BigInt. If the supplied function returns a value, Wasm ignores any types it does not expect.
(import "env" "log" (func $log (param i32) (result)))
A function export is simply named. In JavaScript, instance.exports.log is a regular callable function that runs Wasm when called.
memory imports and exports
A module can declare at most one linear memory (in the current standard). The size is declared in 64 KiB pages. When importing, the supplied WebAssembly.Memory must already meet the declared minimum; when exporting, instance.exports.mem exposes the underlying memory object so the host can read and write raw bytes via ArrayBuffer.
(import "env" "mem" (memory 1 10))
;; minimum 1 page (64 KiB), maximum 10 pages (640 KiB)
Sharing a memory import is the standard way for Wasm and its host to exchange large data without serialization.
table imports and exports
A WebAssembly table is a typed array of references — currently funcref or externref. Tables power call_indirect, the Wasm equivalent of a function pointer dispatch. An imported table, like an imported memory, must satisfy the declared minimum size.
(import "env" "tbl" (table 4 funcref))
The JavaScript WebAssembly.Table API lets you read and write table slots from the host, enabling dynamic function registration patterns.
global imports and exports
Globals are scalar values (i32, i64, f32, f64) that can be shared between the host and the module. An immutable global can be supplied as a plain number (or BigInt for i64). A mutable global must be a WebAssembly.Global constructed with { value: 'i32', mutable: true }.
(import "env" "base" (global $base (mut i32)))
Exporting a mutable global lets the host observe changes the Wasm code makes to it without reading memory.
Tips and notes
- Memory size is in 64 KiB pages; a
(memory 1)is exactly 65536 bytes. i64globals and params surface in JS asBigInt, notNumber. Pass BigInt literals orBigInt(n).- Mutable globals need
new WebAssembly.Global({ value: 'i32', mutable: true }, initialValue). - A table’s element type is
funcref(orexternrefwith the reference-types proposal enabled). - A missing import or wrong type at instantiation throws a
LinkErrorsynchronously — check the import object shape and types before blaming Wasm logic. - Use
WebAssembly.instantiateStreamingfor production (parses the binary as it downloads) andWebAssembly.instantiatewith a pre-fetched buffer in tests or environments without streaming support.