Rendering and embedding

The core pipeline has three boundaries: source elements describe intent, LayoutPass produces immutable pixel fragments, and render_svg serializes those fragments. Rendering does not query elements, resolve units, or load fonts.

Immutability and performance

Gum copies caller-owned source data and treats elements, geometry, and fragments as readonly. Runtime freezing is enabled by default and disabled with NODE_ENV=production. Set GUM_FREEZE=1 or GUM_FREEZE=0 before importing Gum to override that default. Both modes retain input snapshots and validation.

The setting is fixed when the library loads. FREEZE_ENABLED reports it, and freeze_owned(value) lets custom elements apply the same policy to data they own. This helper is shallow and does not copy its argument. Consumers must respect readonly results in both modes because layout caches share those objects.

For browser bundles, define __GUM_FREEZE__ as a boolean. Studio production and MCP viewer builds disable freezing by default; GUM_FREEZE=1 during the build enables it. Browsers without an explicit setting default to enforcement.

From JSX to SVG

evaluate executes JavaScript. Use it only for trusted source, or provide a separate isolation boundary in your application. Its optional scope adds or overrides evaluator bindings. It returns an Element, not an SVG string, and sources ending in a return statement may hand back a plain value instead. render_element below wraps bare elements in Page; the core evaluator does not.

To reuse package exports and shared bindings across figures, configure an Evaluator once:

Every evaluator includes core bindings. Its configured scope overrides those bindings, and per-call scope takes precedence over both. The constructor copies the binding map, retaining the identity of supplied objects and functions. Each call starts fresh local bindings and a random stream; name and seed default to the constructor values and can be overridden per call. evaluator.evaluate_prelude(code) returns declarations for explicit reuse in another call's scope. It does not add them to the evaluator. Font resources still belong to layout and rendering; see math setup.

JSX is optional. Element exports are constructors, so ordinary TypeScript can build the same source graph directly:

Viewports and plain values

Hosts such as the CLI, the editor, and the MCP viewer share one entry point that takes an evaluated result, wraps a bare element in Page, applies host viewport props, lays it out under a request, and serializes it:

The result is tagged. An element yields svg markup, the realized size, the fragment, and the pass that produced it. A Document yields kind: 'document' with a pages array of those SVG results, its title, and the shared pass. Other evaluated values come back as a value for the host to print. Without options a fresh LayoutPass with the core fonts is created. Pass fonts to seed it with another provider, such as the math fonts, or pass to reuse one across renders and keep its cache; fonts given alongside a pass are installed on it. defaults sit beneath the source's own Page props, so a source theme beats a host default, while overrides sit above them for hosts whose theme must win. wrap props apply only to the viewport generated around a bare element, for example max_width and max_height bounds for a preview canvas that an explicit Page should not inherit. Undefined entries in any of the three are ignored, so optional settings can be forwarded directly. An existing Page keeps its layout descriptor and extra props, so custom viewport subclasses survive. request is an ordinary LayoutRequest; the Page aspect and size props resolve against it as in any layout. Use layout_element for the same wrapping and layout without serialization, for example to inspect or rasterize the fragment, and make_viewport for the wrapping step alone.

SVG output uses 10 decimal places for numbers by default. Pass precision from 0 to 100 to change that limit, or 'full' to preserve JavaScript's number strings. The setting affects serialization only; fragments keep their original pixel coordinates. Trailing zeroes are omitted: precision: 3 formats 123.45678 as 123.457 and 1.2 as 1.2. Use 0 to round to whole numbers. inspect_fragment(fragment, { precision }) offers the same formatting for tree inspection. It defaults to 'full'; the CLI tree mode supplies its usual 10 decimal places unless --precision overrides them.

Gum Studio offers 640 × 480 pixels during layout, then scales the resulting SVG to its preview panel. Authored figure dimensions and font sizes determine the composition. See design sizes for a fixed figure that preserves its proportions across display sizes.

To bound the SVG output itself, pass maxima on the generated viewport:

The content lays out using those offers first. If its measured size exceeds a maximum, the viewport uniformly shrinks the whole drawing so both dimensions fit; it does not simply crop the bottom. This includes fonts and strokes. With only a width and no height maximum, text can still grow naturally in height. Use an exact request for a fixed allocation, or advisory requests without maxima when overflow beyond the offered size is acceptable.

Documents and multiple pages

Document collects explicit Page children in output order. It supplies page defaults and metadata, while every page lays out independently. It does not participate in spatial layout or automatically paginate overflowing content.

layout_element also recognizes Document and returns { kind: 'document', pages, title, pass }, where pages is an array of fragments. All pages share the pass and its font resources. Page props override document defaults; host defaults sit beneath both, and host overrides sit above both. Host layout requests apply independently to every page. PDF allows different page sizes; PPTX requires matching sizes.

render_element(document, options) serializes each page as a separate SVG. Its pages entries each contain { kind: 'svg', svg, size, fragment, pass }. The renderer adds -page-1, -page-2, and so on to id_prefix so the SVGs can share one HTML view without colliding definition IDs. An explicit render title overrides the document title.

The CLI exports a complete single-file Document with gum talk.jsx -o talk.pdf or gum talk.jsx -o talk.pptx. Use --page 2 to select a single page for an image, PDF, or PPTX. SVG, PNG, and kitty require a selection for multi-page documents.

Requests and results

pass.layout accepts an optional LayoutRequest. natural means no size offer, available supplies an advisory budget, and exact fixes an allocation. Omitted, undefined, and null axes are natural, so optional dimensions forward directly. Values in these low-level requests are already pixels, unlike source lengths:

An available budget does not itself establish a percentage reference. See Units and Sizing for how containers establish definite boxes and resolve source sizes.

Fragments contain size, drawings, child placements, named guides such as text baselines, ink bounds, and overflow. Size is the allocated rectangle; ink is painted bounds after clipping; overflow records excess content before clipping. inspect_fragment shows this geometry without going through SVG.

Debugging layout

Add debug to a layout element to show its allocated rectangle in solid red and its content rectangle, when available, in dashed blue:

Only the marked element gets outlined; children can opt in separately with their own debug flags. Omit the flag or use debug={false} to turn it off. The same option works in TypeScript, for example new Box({ debug: true, padding: em(1) }).

The allocated rectangle is the element's final layout size. Containers such as Box expose a separate content rectangle inside their insets; elements without one show only the allocated rectangle. The overlays follow rotations and fitting, appear above the artwork, and bypass content clipping within the viewport. They leave layout, ink bounds, and overflow unchanged. SVG, PNG, terminal, PDF, PPTX, and MP4 output all include the overlays.

Before rendering, the shared prepare_render(fragment) step converts debug flags into ordinary path drawings in a final overlay fragment. It resolves placements into viewport coordinates so fitting does not scale the outline strokes. The exporters call this step automatically; a custom renderer can call it too. The original layout fragment retains its metadata, and preparing an already prepared fragment does not add another overlay.

Inline Span styling is folded into its owning Text; mark the Text to inspect its allocation.

The middle stage in the runnable example below uses debug to show both boxes.

Reuse and output

Keep element identities and a LayoutPass to reuse cached results for identical requests, inherited styles, reference boxes, and resource versions. Creating a new equivalent element or a new pass does not preserve that cache. Text also caches prepared glyphs within a pass so a new width can reuse shaping. This is query caching, not a promise that every layout or edit is sublinear.

render_svg accepts title, background, and id_prefix. Use distinct prefixes when embedding multiple generated SVGs in one HTML document, so clip definitions do not collide. Prefixes start with a letter or underscore and then contain letters, digits, underscores, dots, or hyphens. Text is emitted as paths.

For PNG and terminal graphics, install gum-jsx and use the CLI. Those host concerns are separate from core layout. The runnable source below is a small diagram to feed through this pipeline, not a host script.