Skip to content
D
Documentation

Vnode

reference
4 min readUpdated

Import these from @grafloria/renderer.

Functions

camelToKebab

camelCase → kebab-case (strokeWidth → stroke-width).

ts
function camelToKebab(str: string): string

createDomElement

Build a fresh detached DOM tree for vnode using the default patcher.

ts
function createDomElement(vnode: VNode, namespace: string = SVG_NS): Element

createForeignObject

Create a foreignObject VNode

Creates a VNode representing an SVG foreignObject element, which allows embedding HTML content inside SVG. Automatically generates a container ID if not provided, and includes a default XHTML div wrapper.

ts
function createForeignObject(options: ForeignObjectOptions): VNode

Parameters

  • options: Configuration options for the foreignObject

Returns A VNode of type 'foreignObject'

Example

typescript
const vnode = createForeignObject({
  nodeId: 'node-1',
  x: 10,
  y: 20,
  width: 200,
  height: 150
});
// Returns: { type: 'foreignObject', props: { x: 10, y: 20, ... }, children: [...] }

Example

With custom container ID and children

typescript
const vnode = createForeignObject({
  nodeId: 'node-1',
  x: 10,
  y: 20,
  width: 200,
  height: 150,
  containerId: 'my-custom-id',
  children: [
    { type: 'div', props: { className: 'custom-content' } }
  ]
});

getContainerId

Get the container ID from a foreignObject VNode

Extracts the container ID from a foreignObject VNode's props. Returns undefined if the VNode is not a foreignObject or doesn't have a container ID.

ts
function getContainerId(vnode: VNode): string | undefined

Parameters

  • vnode: The VNode to extract the container ID from

Returns The container ID if the VNode is a foreignObject, undefined otherwise

Example

typescript
const vnode = createForeignObject({
  nodeId: 'node-1',
  x: 0, y: 0, width: 100, height: 100
});

const containerId = getContainerId(vnode);
// Returns: 'fo-node-1-1'

Example

Non-foreignObject returns undefined

typescript
const rectNode = { type: 'rect', props: { ... } };
const containerId = getContainerId(rectNode);
// Returns: undefined

isForeignObject

Check if a VNode is a foreignObject element

Type guard function that checks if the given VNode represents a foreignObject element.

ts
function isForeignObject(vnode: VNode): boolean

Parameters

  • vnode: The VNode to check

Returns True if the VNode is a foreignObject, false otherwise

Example

typescript
const foNode = createForeignObject({ ... });
const rectNode = { type: 'rect', props: { ... } };

isForeignObject(foNode);   // true
isForeignObject(rectNode); // false

isOpaqueVNode

foreignObject subtrees embed live HTML (framework components, form controls, media). They are OPAQUE to the diff: props are patched, children are left exactly as they are. Diffing into them would wipe whatever was mounted there.

ts
function isOpaqueVNode(vnode: VNode): boolean

reconcile

Reconcile vnode into container using the default patcher.

ts
function reconcile(container: Element, vnode: VNode): Element

serializeStyle

Serialize a style prop. Accepts the object form the renderer emits ({ cursor: 'move' }) as well as a plain string. Returns '' for empty/absent styles.

ts
function serializeStyle(style: unknown): string

Classes

ContainerIdGenerator

Generate unique container IDs for foreignObject elements

This class provides static methods to generate and validate container IDs used to link SVG foreignObject elements with their Angular component content.

ts
class ContainerIdGenerator

Methods

  • static generate(nodeId: string): string (static) — Generate a unique container ID for a foreignObject element
  • static isContainerId(id: string): boolean (static) — Check if a given ID is a valid container ID

Container IDs must start with 'fo-' prefix. This is a lightweight check that only validates the prefix.

  • static getNodeId(containerId: string): string | null (static) — Extract the node ID from a container ID

Parses a container ID and returns the original node ID. Container IDs must follow the format: fo-{nodeId}-{counter}

  • static reset(): void (static) — Reset the internal counter to zero

This method is primarily for testing purposes to ensure predictable ID generation in test suites.

VNodePatcher

Keyed VNode → DOM reconciler.

ts
const patcher = new VNodePatcher();
patcher.reconcile(container, vnodeTree);   // first call: mount
patcher.reconcile(container, nextTree);    // later calls: diff + patch in place
ts
class VNodePatcher

Methods

  • constructor(options: VNodePatcherOptions = {})
  • get stats(): Readonly<PatchStats> — Work done during the most recent reconcile() call.
  • reconcile(container: Element, vnode: VNode): Element — Diff vnode against whatever this patcher last rendered into container and patch the existing DOM in place. First call (or a lost root) mounts a fresh tree.
  • hydrate(container: Element, vnode: VNode): Element — ADOPT the DOM already inside container as the materialization of vnode, without creating, moving or removing a single node.

This is the client half of SSR hydration. The server emitted the serialization of this exact VNode tree into the HTML; re-mounting it would mean tearing that DOM down and rebuilding it — the "hydration flash" every flow library ships with. Instead we simply register the existing root as our mount, so the NEXT reconcile() diffs against vnode and touches only what genuinely changed.

The adopt is only taken when the existing root plausibly IS this tree (same tag name); otherwise we fall back to a normal mount, which is always correct (just not flash-free). Assert with stats: a real hydration reports created: 0, removed: 0.

  • getMountedElement(container: Element): Element | undefined — The root element currently mounted in container, if any.
  • unmount(container: Element): void — Remove the mounted tree and forget the container.
  • createElement(vnode: VNode, namespace: string = SVG_NS): Element — Build a fresh detached DOM element (deep) for a VNode.
  • patchElement(el: Element, oldVNode: VNode, newVNode: VNode): Element — Diff two VNodes onto an existing element.
  • patchProps( el: Element, oldProps: Record<string, any>, newProps: Record<string, any> ): void — Apply a prop delta to an element (no children touched).

Constants

defaultPatcher

Process-wide default patcher — convenient for the common "one DOM, one tree" case. Instantiate VNodePatcher directly for isolated instances.

ts
const defaultPatcher: VNodePatcher

SVG_NS

SVG namespace — everything outside a foreignObject is created here.

ts
const SVG_NS: "http://www.w3.org/2000/svg"

XHTML_NS

XHTML namespace — foreignObject children are HTML, not SVG.

ts
const XHTML_NS: "http://www.w3.org/1999/xhtml"

Interfaces

ForeignObjectOptions

Options for creating a foreignObject VNode

ts
interface ForeignObjectOptions

Properties

NameTypeDefaultDescription
nodeIdstringNode ID - used to generate container ID
xnumberX coordinate (top-left corner)
ynumberY coordinate (top-left corner)
widthnumberWidth in pixels
heightnumberHeight in pixels
containerId?stringOptional custom container ID If not provided, will be auto-generated using ContainerIdGenerator
children?VNode[]Optional children VNodes If not provided, creates a default XHTML div wrapper
key?stringOptional key for React/Angular diffing optimization

PatchStats

Per-reconcile work counters. Reset at the top of every reconcile() call. Useful as a cheap regression guard: a steady-state frame should create ~0 elements ("no teardown-and-rebuild").

ts
interface PatchStats

Properties

NameTypeDefaultDescription
creatednumberDOM nodes created from scratch.
reusednumberDOM nodes reused in place (patched, not recreated).
movednumberReused DOM nodes that had to move to a new sibling index.
removednumberDOM nodes removed because their VNode disappeared.
skippednumberSubtrees skipped entirely because the VNode object was identical.

VNodePatcherOptions

Options for a patcher instance.

ts
interface VNodePatcherOptions

Properties

NameTypeDefaultDescription
document?DocumentDocument used to create nodes. Defaults to the ambient globalThis.document. Pass one explicitly for headless / SSR / multi-document use.

Types

VNodeChild

A child slot in a VNode tree. Strings/numbers materialise as text nodes.

ts
type VNodeChild = VNode | string | number | null | undefined;

Members

  • toString(): string — Returns a string representation of a string.
  • valueOf(): string — Returns the primitive value of the specified object.
  • toLocaleString(): string — Returns a date converted to a string using the current locale.

Was this page helpful?

Vnode — Grafloria