WebUI WebAssembly #

WebUI provides browser-ready WebAssembly bindings through wasm-bindgen. The bindings are built as three variants so you can choose only the parser, only the handler, or the combined playground bundle.

Variants #

VariantImport pathExportsUse when
Handlerwasm/handler/webui_wasm_handler.jsProtocolYou already have protocol bytes and only need rendering
Parserwasm/parser/webui_wasm_parser.jsbuild_protocolYou need to compile virtual browser files into protocol bytes
Allwasm/all/webui_wasm_all.jsParser and handler exportsYou need both sides in one module, such as the docs playground

The handler-only bundle excludes webui-parser, and the parser-only bundle excludes webui-handler. The combined bundle keeps the previous playground behavior.

Building the WASM bundles #

cargo xtask build-wasm

The output is generated under docs/.webui-press/public/wasm/ for the documentation playground and release staging. Rebuild it when Rust code in webui-wasm, webui-parser, webui-handler, or webui-protocol changes.

Handler-only API #

Use the handler bundle when the protocol was built elsewhere and loaded as protobuf bytes in the browser.

import init, { Protocol } from './wasm/handler/webui_wasm_handler.js';

await init();

const protocolBytes = new Uint8Array(await (await fetch('/protocol.bin')).arrayBuffer());
const protocol = new Protocol(protocolBytes, 'webui');
const html = protocol.render(
  '{"title": "Hello"}',
  { entry: 'index.html', requestPath: '/' },
);

Keep the Protocol instance alive across renders. It decodes protobuf, builds deterministic indices, and binds the plugin once.

Protocol #

MethodDescription
render(stateJson, options?)Return complete rendered HTML as a string
renderStream(stateJson, onChunk, options?)Invoke callbacks coalesced around a 16 KiB target
renderPartial(stateJson, entry, requestPath, inventoryHex)Return a complete JSON partial response with active-route projected state
renderComponentTemplates(componentTags, inventoryHex)Return requested template payloads and updated inventory
tokens()Return CSS token names in build order

For a complete static/CDN service worker example using this callback to write a ReadableStream and mirror --theme token injection in the browser, see Serverless Architecture.

Parser-only API #

Use the parser bundle when browser code needs to compile an in-memory file map into protocol bytes.

import init, { build_protocol } from './wasm/parser/webui_wasm_parser.js';

await init();

const files = {
  'index.html': '<h1>{{title}}</h1>',
  'my-card.html': '<p><slot></slot></p>',
  'my-card.css': 'p { color: red; }',
};

const protocolBytes = build_protocol(
  files,
  'index.html',
  [projectionManifest],
);

build_protocol(files, entry, projectionManifests?) #

Parse virtual files into a WebUI protocol without rendering.

ParameterTypeDescription
filesRecord<string, string>Map of filenames to content
entrystringEntry HTML filename
projectionManifestsobject[]Optional bundler manifest fragments

Returns protobuf-serialized WebUIProtocol as a Uint8Array. Throws on missing entry files, invalid templates, invalid component authoring, or protocol serialization errors.

Without manifests, initial and scripted navigation state remain full. With manifests, WASM applies the shared schema/build-ID validation, fragment merge, and strict coverage rules. Because virtual WASM builds have no filesystem, they cannot repeat disk stale-file checks and never analyze JavaScript.

Component discovery follows the virtual file map convention: HTML files with a hyphen in the name are registered as components, such as my-card.html for <my-card>. Matching .css files are paired and inlined with CssStrategy::Style.

Combined API #

Use the combined bundle when you want parser and handler exports from one module.

import init, { build_protocol, Protocol } from './wasm/all/webui_wasm_all.js';

await init();

const protocolBytes = build_protocol(files, 'index.html');
let html = '';
const protocol = new Protocol(protocolBytes);
protocol.renderStream(stateJson, (chunk) => {
  html += chunk;
}, { entry: 'index.html', requestPath: '/' });

The documentation playground imports this combined bundle and uses build_protocol followed by new Protocol(...) so it can measure compile and render time separately.

Differences from server-side rendering #

AspectServer (CLI / Rust / Node)WASM (Browser)
Protocol formatProtobuf binaryProtobuf bytes
CSS strategyLink by default, Style or Module when configuredStyle for virtual file builds
File I/OFilesystem and component discovery sourcesVirtual file map
StreamingSupported by native handlersProtocol.renderStream() calls a batched JavaScript callback
Bundle choiceNative crates/addonsHandler-only, parser-only, or combined WASM