Skip to content

Core Concepts

Generative DOM is built around a few key ideas. Understanding them will help you use the library effectively and write your own plugins.

Streaming

Generative DOM is designed for input that arrives incrementally. The push() method accepts arbitrary chunks of text -- a single character, a partial word, an entire paragraph. The parser handles chunks that split markdown at any point, including mid-syntax (**bol + d**), mid-escape (\ + *), and mid-link ([text](ur + l)).

When the tokenizer encounters ambiguous trailing content, it preserves the unconsumed portion in the buffer. The next push() appends new content and tokenization retries from where it left off.

Plugins

Generative DOM's core knows nothing about markdown. All syntax recognition and rendering is handled by plugins. The core is a plugin host that provides:

  • A buffer and tokenizer loop
  • An object pool for DOM element reuse
  • A scheduler for batched rendering
  • An event system for inter-plugin communication

Each plugin declares a priority number that determines matching order. Lower numbers are matched first. Recommended ranges:

RangePurpose
0--99Custom syntax (matched first)
100--199Block-level markdown (headings, code, lists, quotes)
200--299Inline markdown (bold, italic, strikethrough)
300+Fallback (paragraphs, plain text)

When multiple plugins have the same priority, registration order breaks the tie.

The Rendering Pipeline

Input chunks --> Buffer --> Tokenizer --> Parser --> AST diff --> Renderer --> DOM
                  ^                                                |
                  +---- rAF + debounce scheduling -----------------+
  1. Buffer: push() appends text to an internal buffer and marks it dirty.
  2. Tokenizer: Iterates plugins by priority. The first plugin to return a match wins. Consumes matched characters and produces a Token.
  3. Parser: Builds a flat AST (array of block tokens, each may contain inline tokens). On each render cycle, it diffs against the previous AST.
  4. Renderer: Only updates DOM nodes for changed, new, or removed tokens. Uses the object pool for element recycling.
  5. Scheduler: push() does not render immediately. A requestAnimationFrame loop checks the dirty flag, respects debounceMs, and triggers a parse-and-render cycle.

Tokens

A Token is the intermediate representation between parsing and rendering:

ts
interface Token {
  type: string;           // Identifies which plugin handles rendering
  raw: string;            // Original matched markdown
  children?: Token[];     // Nested tokens (e.g., list items)
  content?: string;       // Inner text content
  meta?: Record<string, unknown>; // Plugin-specific data
}

Block-level plugins produce tokens via matchBlock(). Inline-level plugins produce tokens via matchInline(). The core routes each token to the plugin whose type matches for rendering.

Object Pool

Generative DOM recycles DOM elements to avoid garbage collection pressure during rapid streaming. When a token is removed from the AST, its DOM element is returned to the pool. When a new token of the same tag is needed, the pool provides a pre-existing element instead of creating one.

ts
interface ObjectPool {
  acquire(tag: string): HTMLElement;
  release(element: HTMLElement): void;
  drain(): void;
  stats(): { pooled: number; active: number; created: number; reused: number };
}

Plugins access the pool through the RenderContext provided to their render() method.

Events

Plugins can emit events through the plugin context. Events are namespaced by plugin name automatically:

ts
// Inside a plugin:
ctx.emit('progress', { value: 35 });
// Subscribers receive it as "pluginName:progress"

// Subscribing from application code:
md.on('events:progress', (data) => {
  console.log(data); // { value: 35 }
});

Events are dispatched synchronously. If a listener throws, other listeners still execute.

Security Model

Generative DOM enforces security at the architecture level:

  • All DOM construction uses document.createElement and document.createTextNode -- never innerHTML
  • Link href values are whitelisted to https:, http:, and mailto: protocols
  • Image src values are whitelisted to https: and http:
  • No on* event handler attributes are allowed on any element
  • Custom element attribute whitelists are enforced by the core

See the Security Guide for details.

Next: Streaming Guide