@generative-dom/svelte
v0.1.0
Published
Official Svelte wrapper for Generative DOM streaming markdown renderer
Downloads
31
Maintainers
Readme
@generative-dom/svelte
Official Svelte wrapper for the Generative DOM streaming markdown renderer.
A thin (~150 LOC) pure-TypeScript layer over @generative-dom/core that handles Svelte lifecycle plumbing for you. Two public APIs:
createGenerativeDom({ container, plugins })— framework-agnostic factory returning{ push, flush, reset, destroy, on, off }. Call from inside$effect(Svelte 5) oronMount(Svelte 4).generativeDomAction— Svelte action applied viause:generativeDomAction={{ plugins, markdown }}that manages everything declaratively.
Svelte is a peer dependency. The wrapper supports Svelte 4 and Svelte 5. The package ships no .svelte SFC files — adopters don't need a Svelte preprocessor or bundler plugin to consume it.
Installation
pnpm add @generative-dom/svelte @generative-dom/core
# plus whatever plugins you want, e.g.:
pnpm add @generative-dom/plugin-markdown-base @generative-dom/plugin-markdown-headingAction usage (declarative)
<script lang="ts">
import { generativeDomAction } from '@generative-dom/svelte';
import { markdownBase } from '@generative-dom/plugin-markdown-base';
import { markdownHeading } from '@generative-dom/plugin-markdown-heading';
const plugins = [markdownBase(), markdownHeading()];
export let text: string;
</script>
<div class="prose" use:generativeDomAction={{ plugins, markdown: text }}></div>When markdown changes the action calls reset() then push() then flush() on the underlying instance, so the container always reflects the latest string. When plugins, debounceMs, or onError change the Generative DOM instance is destroyed and re-created. On element removal destroy() is called.
Parameters
| Parameter | Type | Notes |
| --------- | ---- | ----- |
| plugins | GenerativeDomPlugin[] | Required. Identity changes trigger a rebuild. |
| markdown | string | Optional. Triggers reset + push + flush when it changes. |
| debounceMs | number | Default 16 (~1 frame). |
| onError | (error) => void | Forwarded to Generative DOM's onError. |
Factory usage (streaming)
For streaming sources (LLM SDKs, server-sent events) call the factory directly so you can pump chunks:
<script lang="ts">
import { onMount, onDestroy } from 'svelte';
import { createGenerativeDom, type GenerativeDomHandle } from '@generative-dom/svelte';
import { markdownBase } from '@generative-dom/plugin-markdown-base';
import { markdownHeading } from '@generative-dom/plugin-markdown-heading';
export let stream: AsyncIterable<string>;
let el: HTMLDivElement;
let handle: GenerativeDomHandle | undefined;
onMount(async () => {
handle = createGenerativeDom({
container: el,
plugins: [markdownBase(), markdownHeading()],
});
for await (const chunk of stream) handle.push(chunk);
handle.flush();
});
onDestroy(() => handle?.destroy());
</script>
<div bind:this={el} class="prose"></div>The factory returns:
{
push: (chunk: string) => void;
flush: () => void;
reset: () => void;
destroy: () => void;
on: (event: string, handler) => void;
off: (event: string, handler) => void;
}Calling any method after destroy() is a safe no-op.
Svelte 5 $effect recipe
<script lang="ts">
import { createGenerativeDom, type GenerativeDomHandle } from '@generative-dom/svelte';
import { markdownBase } from '@generative-dom/plugin-markdown-base';
let el: HTMLDivElement;
let { text }: { text: string } = $props();
$effect(() => {
const handle: GenerativeDomHandle = createGenerativeDom({
container: el,
plugins: [markdownBase()],
});
handle.push(text);
handle.flush();
return () => handle.destroy();
});
</script>
<div bind:this={el} class="prose"></div>Why no .svelte SFC?
Shipping a Svelte SFC would require adopters to run a Svelte preprocessor on our node_modules output, or force us into a dual-publishing setup. The action + factory pair covers every idiomatic Svelte usage (Svelte 4 use:, Svelte 5 attachments, $effect, onMount) without any compilation dependency.
Why a wrapper?
@generative-dom/core is framework-agnostic and does not depend on Svelte. The wrapper exists so Svelte users don't have to write the lifecycle plumbing themselves — it's the same code you would write, just maintained centrally.
License
MIT
