Appearance
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:
| Range | Purpose |
|---|---|
| 0--99 | Custom syntax (matched first) |
| 100--199 | Block-level markdown (headings, code, lists, quotes) |
| 200--299 | Inline 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 -----------------+- Buffer:
push()appends text to an internal buffer and marks it dirty. - Tokenizer: Iterates plugins by priority. The first plugin to return a match wins. Consumes matched characters and produces a Token.
- Parser: Builds a flat AST (array of block tokens, each may contain inline tokens). On each render cycle, it diffs against the previous AST.
- Renderer: Only updates DOM nodes for changed, new, or removed tokens. Uses the object pool for element recycling.
- Scheduler:
push()does not render immediately. ArequestAnimationFrameloop checks the dirty flag, respectsdebounceMs, 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.createElementanddocument.createTextNode-- neverinnerHTML - Link
hrefvalues are whitelisted tohttps:,http:, andmailto:protocols - Image
srcvalues are whitelisted tohttps:andhttp: - 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