Logo

Vanilla DOM

@smoothstream/dom is the framework-free adapter. It creates one managed root inside a container and reconciles the same keyed HTML model the React and Vue adapters use.

npm install @smoothstream/dom
pnpm add @smoothstream/dom
yarn add @smoothstream/dom
bun add @smoothstream/dom

There is no framework peer dependency. Reveal CSS and the default prose theme import automatically.

Create, update, destroy

assistant-message.ts
import { createSmoothstream } from "@smoothstream/dom"; const stream = createSmoothstream(document.querySelector("#response")!, {  className: "assistant-markdown",  receiving: true,}); stream.update(markdownReceivedSoFar, { receiving: true });stream.update(completeMarkdown, { receiving: false }); stream.destroy();
MethodRole
createSmoothstream(container, options)Mounts a root inside container and returns a controller.
update(markdown, options?)Queues the latest append-only snapshot. options.receiving is the only value you can change after create.
destroy()Removes the root, listeners, pending work, and the completion announcer.
elementThe managed HTMLDivElement.

Options are mostly constructor-only

mode, reveal, interval, duration, reducedMotion, unstyled, className, and codeHighlighter are read when the controller is created. Changing them later has no effect.

To present a different response, or to use different timing, call destroy() and create a new controller.

const history = createSmoothstream(historyEl, { mode: "static" });history.update(previousMessage.content); const live = createSmoothstream(liveEl, { receiving: true });live.update(partial, { receiving: true });// Keep this controller in streaming mode when input closes:live.update(complete, { receiving: false });

update() throws unless the new string starts with the previous source. It also throws after destroy(). Abort requests and ignore late callbacks when removing the view; the streaming example shows both steps.

If receiving is omitted from an update, the controller keeps its previous value. After creating with receiving: true, explicitly pass { receiving: false } to close input.

The controller owns the root returned as element. Keep application UI outside that managed subtree and let Smoothstream reconcile its contents.

Completion callbacks

Pass onRevealComplete and onPresentationComplete when creating the controller. The first runs once when the final unit begins appearing after input closes; the second runs once after entrances finish and temporary reveal nodes compact. See Completion callbacks for the timing details and a two-controller handoff example.

Full option list: API.

© 2026 Smoothstream. All rights reserved.

Built withRadiant Docs