Skip to content

interactive

Renders interactive elements with event handlers and persistent state across re-renders. Demonstrates that the plugin system preserves interactivity even when the streaming renderer updates surrounding content.

Details

PropertyValue
Nameinteractive
Priority45
TypeBlock
Factoryinteractive()

Syntax

markdown
<button>Click me</button>
<toggle label="Dark mode" />
<input placeholder="Type here..." />

Built-in Interactive Elements

<md-button>

Renders an HTML <button> element. Emits a button-click event on each click with the button text and cumulative click count.

markdown
<button>Click me</button>
ts
md.on('interactive:button-click', (data) => {
  // data: { text: "Click me", clickCount: 3 }
});

<md-toggle>

Renders a checkbox with a label. Emits a toggle event when the state changes.

markdown
<toggle label="Dark mode" />
<toggle label="Notifications" checked />
AttributeTypeDefaultDescription
labelstring""Label text displayed next to the checkbox
checkedbooleanfalseInitial checked state
ts
md.on('interactive:toggle', (data) => {
  // data: { label: "Dark mode", checked: true }
});

<md-input>

Renders an <input> element. Emits an input-change event on input.

markdown
<input placeholder="Type here..." />
<input placeholder="Name" value="Default" />
AttributeTypeDefaultDescription
placeholderstring""Placeholder text
valuestring""Initial value
ts
md.on('interactive:input-change', (data) => {
  // data: { value: "typed text" }
});

State Preservation

Interactive elements maintain their state across re-renders. When new markdown chunks arrive and surrounding content changes, the plugin ensures:

  • Button click counts are not reset
  • Input field text is preserved
  • Toggle state is maintained

This is achieved through stable element identity keyed on the token's position in the stream. The plugin tracks live elements and reuses them during re-renders instead of creating new ones.

API

ts
import { interactive } from '@generative-dom/plugins';

const plugin = interactive();

No configuration options.

Edge Cases

  • State across push(): After pushing new chunks, interactive elements from earlier chunks retain their state (click counts, typed text, toggle state).
  • reset() behavior: Calling md.reset() properly destroys interactive elements and cleans up event listeners.
  • Rapid re-renders: Fast streaming does not duplicate interactive elements.
  • End-of-stream elements: An interactive element at the end of the stream, followed by more chunks, stays in place while new content appears after it.
  • Memory leaks: Creating and destroying many interactive elements does not leak memory. The object pool and cleanup() method handle resource release.

Rendering

SyntaxOutput ElementEvents
<button>text</button><button>interactive:button-click
<toggle label="..." /><input type="checkbox"> + <label>interactive:toggle
<input placeholder="..." /><input>interactive:input-change