@generative-dom/react
v0.1.0
Published
Official React wrapper for Generative DOM streaming markdown renderer
Downloads
76
Maintainers
Readme
@generative-dom/react
Official React wrapper for the Generative DOM streaming markdown renderer.
A thin (~150 LOC) layer over @generative-dom/core that handles the React lifecycle for you. Two public APIs:
<GenerativeDomRenderer>— declarative component for the common "render this markdown string" case.useGenerativeDom()— imperative hook for streaming sources (LLM SDKs, server-sent events, etc.).
React is a peer dependency. The wrapper supports React 18 and React 19.
Installation
pnpm add @generative-dom/react @generative-dom/core
# plus whatever plugins you want, e.g.:
pnpm add @generative-dom/plugin-markdown-base @generative-dom/plugin-markdown-headingComponent usage
import { GenerativeDomRenderer } from '@generative-dom/react';
import { markdownBase } from '@generative-dom/plugin-markdown-base';
import { markdownHeading } from '@generative-dom/plugin-markdown-heading';
const plugins = [markdownBase(), markdownHeading()];
export function Message({ text }: { text: string }) {
return (
<GenerativeDomRenderer
markdown={text}
plugins={plugins}
className="prose"
/>
);
}When markdown changes the component calls reset() then push() then flush() on the underlying instance, so the container always reflects the latest string. When plugins changes (rare) the Generative DOM instance is destroyed and re-created. On unmount destroy() is called.
Props
| Prop | Type | Notes |
| ---- | ---- | ----- |
| plugins | GenerativeDomPlugin[] | Required. Memoize this — identity changes recreate Generative DOM. |
| markdown | string | Optional. Triggers reset + push + flush when it changes. |
| className | string | Applied to the container div. |
| style | CSSProperties | Applied to the container div. |
| debounceMs | number | Default 16 (~1 frame). |
| onEvent | (name, data) => void | Receives plugin events (button-click, link-click, etc.). |
| onError | (error) => void | Forwarded to Generative DOM's onError. |
Hook usage (streaming)
import { useEffect, useMemo } from 'react';
import { useGenerativeDom } from '@generative-dom/react';
import { markdownBase } from '@generative-dom/plugin-markdown-base';
import { markdownHeading } from '@generative-dom/plugin-markdown-heading';
export function StreamedAnswer({ stream }: { stream: AsyncIterable<string> }) {
const plugins = useMemo(() => [markdownBase(), markdownHeading()], []);
const { ref, push, flush, reset } = useGenerativeDom({ plugins });
useEffect(() => {
reset();
(async () => {
for await (const chunk of stream) push(chunk);
flush();
})();
}, [stream, push, flush, reset]);
return <div ref={ref} className="prose" />;
}The hook returns:
{
ref: (el: HTMLElement | null) => void; // attach to your container
push: (chunk: string) => void;
flush: () => void;
reset: () => void;
on: (event: string, handler) => void;
off: (event: string, handler) => void;
}Memoize the plugins array so the underlying Generative DOM instance is not destroyed and re-created on every render.
Why a wrapper?
@generative-dom/core is framework-agnostic and does not depend on React. The wrapper exists so React users don't have to write the useEffect lifecycle plumbing themselves — it is the same code you would write, just maintained centrally.
License
MIT
