# In-Browser Compilation Compile Mermaid in the browser or an edge runtime when the source is only known at runtime. ## Overview Most pages compile diagrams at build time. Editors, previews, and diagrams from users need the compiler at runtime. `ondine/compiler/wasm` supplies this compiler, and the caller supplies the WebAssembly module. ```ts title="preview.ts" import { create } from 'ondine/compiler/wasm' import url from 'ondine/compiler/wasm/all.wasm?url' // Compiles the size-optimized module while it downloads const all = await WebAssembly.compileStreaming(fetch(url)) const renderer = create({ modules: { all } }) const { svg } = renderer.render('flowchart LR\n A --> B') ``` The JavaScript glue in `ondine/compiler/wasm` is about 1 kB gzip. The `all.wasm` module holds every diagram type, at about 630 kB gzip. ## Walkthrough The steps below make a live preview. The preview renders the diagram again after each change to the source. ### Load the Module Fetch `all.wasm` and compile it with `WebAssembly.compileStreaming`. The browser compiles the module while it downloads, and it caches the compiled code. The server must send `application/wasm`. ```ts title="preview.ts" import { create } from 'ondine/compiler/wasm' import url from 'ondine/compiler/wasm/all.wasm?url' // Emits all.wasm as an asset and compiles it on first use const all = await WebAssembly.compileStreaming(fetch(url)) const renderer = create({ modules: { all } }) ``` ```ts title="preview.ts" import { create } from 'ondine/compiler/wasm' // Serves node_modules/ondine/dist/internal/all.generated.wasm at /ondine/all.wasm const all = await WebAssembly.compileStreaming(fetch('/ondine/all.wasm')) const renderer = create({ modules: { all } }) ``` ### Render on Input Each `render` call compiles synchronously. A renderer keeps its module instance. Create the renderer once and use it for each input event. ```ts title="preview.ts" import { RenderError } from 'ondine/compiler/wasm' const input = document.querySelector('textarea')! const output = document.getElementById('preview')! input.addEventListener('input', () => { try { // Replaces the preview with the new diagram output.innerHTML = renderer.render(input.value, { maxWidth: output.clientWidth }).svg } catch (error) { if (!(error instanceof RenderError)) throw error output.textContent = error.message } }) ``` ### Show Errors `RenderError` has the line, the column, and a snippet with a caret. These values match the diagnostics of Mermaid. ```mermaid stateDiagram-v2 [*] --> Editing Editing --> Rendered: valid source Editing --> Error: RenderError Error --> Editing: input ``` ## Recipes ### Cloudflare Workers Cloudflare Workers reject WebAssembly compilation at runtime. Import a `.wasm` file so that the bundler compiles the module ahead of time. Then pass the `WebAssembly.Module` to `create`. ```ts title="worker.ts" import { create } from 'ondine/compiler/wasm' import all from 'ondine/compiler/wasm/all.wasm' // Uses the precompiled module, so no compilation occurs at runtime const renderer = create({ modules: { all } }) ``` ### Load One Type If a page compiles only one diagram type, load the module for that type, such as `flowchart.wasm`. This module is much smaller than `all.wasm`. [Diagram Modules](/docs/api/compiler-wasm/types) lists every type. ```ts title="preview.ts" import { create } from 'ondine/compiler/wasm' import url from 'ondine/compiler/wasm/flowchart.wasm?url' // Renders flowcharts and throws RenderError for other types const flowchart = await WebAssembly.compileStreaming(fetch(url)) const renderer = create({ modules: { flowchart } }) ``` ### Inline the Module The `ondine/compiler/wasm/all` entrypoint inlines `all.wasm` as base64, at about 830 kB gzip. It needs no file server, but the page downloads more, and `create` compiles the bytes only after the whole module loads. ```ts title="preview.ts" import { create } from 'ondine/compiler/wasm' // Splits the base64 module into its own chunk const { wasm } = await import('ondine/compiler/wasm/all') const renderer = create({ modules: { all: wasm } }) ``` ### Offload Compilation A large diagram can take milliseconds to compile. Move the renderer into a Web Worker. Then post the SVG back to the page. ```ts title="compile.worker.ts" import { create } from 'ondine/compiler/wasm' import url from 'ondine/compiler/wasm/all.wasm?url' // Starts compiling at once, so that early messages wait for the renderer const renderer = WebAssembly.compileStreaming(fetch(url)).then((all) => create({ modules: { all } }), ) // Responds to each message with the compiled SVG self.onmessage = async (event: MessageEvent) => { self.postMessage((await renderer).render(event.data).svg) } ``` ## More