npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@alphanimble/streamhtml

v0.1.0

Published

A streaming-optimized HTML renderer for AI responses — handles incomplete HTML gracefully

Readme

StreamHtml

A streaming-optimized HTML renderer for AI responses — the HTML counterpart to Streamdown.

AI models excel at writing HTML. It's more expressive than markdown: metric dashboards, comparison grids, styled diffs, rich tables, badges, and custom layouts — all in one stream. StreamHtml renders that HTML safely while it streams, handling incomplete tags gracefully.

Demo

Screen recording of the included chat demo (npm run chat) streaming a live OpenRouter response as HTML.

GitHub READMEs do not render <iframe> or <video> embeds (sanitized HTML only). Use the thumbnail below — click to watch on Streamable:

Watch the StreamHtml demo on Streamable

Direct link: streamable.com/fxnzq6

What you're seeing in the recording:

  • Incremental HTML repair — incomplete tags and unclosed elements are handled as tokens arrive, without layout jumps
  • Live DOM patching — text and table rows append in place instead of replacing the whole message on every chunk
  • Reasoning panel — model thinking tokens stream in a collapsible block before HTML content starts
  • Streaming caret — a smooth block cursor tracks the active text insertion point (only while characters are landing)
  • Text fade-in — each new chunk of streamed text fades from half-opacity to full
  • Stable blocks — completed sections freeze so earlier content doesn't re-render during the stream
  • Sanitized output — DOMPurify strips unsafe markup before anything hits the DOM

Features

  • Streaming-first — Strips incomplete tags, auto-closes open elements, splits stable/live blocks
  • Incremental DOM patching — Appends text and table rows without full re-renders
  • Reasoning tokens — Optional collapsible thinking block separate from HTML content
  • Streaming caret — Smooth block cursor that follows live text insertion
  • Performance — Memoizes completed blocks so they never re-render during streaming
  • Security — DOMPurify sanitization with secure defaults (no scripts, no event handlers)
  • Drop-in React component — Works with AI SDK, any chat UI, or plain streaming text
  • Headless core — Use rehtml() without React for Node or other frameworks
  • Raw-text aware — Respects <pre>, <code>, etc. where < is literal

Install

npm install @alphanimble/streamhtml

Requires React 18+ (and react-dom in browser apps). Import the base styles once:

import "@alphanimble/streamhtml/styles.css";

Quick start

import { StreamHtml } from "@alphanimble/streamhtml";
import "@alphanimble/streamhtml/styles.css";

function Message({ content, isStreaming }: { content: string; isStreaming: boolean }) {
  return (
    <StreamHtml isStreaming={isStreaming}>
      {content}
    </StreamHtml>
  );
}

With reasoning tokens

<StreamHtml
  isStreaming={isStreaming}
  reasoning={reasoningText}
  thinkingLabel="Thinking"
>
  {htmlContent}
</StreamHtml>

With AI SDK

import { useChat } from "@ai-sdk/react";
import { StreamHtml } from "@alphanimble/streamhtml";
import "@alphanimble/streamhtml/styles.css";

export function Chat() {
  const { messages, status } = useChat();

  return (
    <div>
      {messages.map((message) => (
        <div key={message.id}>
          {message.parts.map((part, i) =>
            part.type === "text" ? (
              <StreamHtml
                key={i}
                isStreaming={
                  status === "streaming" &&
                  message.id === messages.at(-1)?.id
                }
              >
                {part.text}
              </StreamHtml>
            ) : null,
          )}
        </div>
      ))}
    </div>
  );
}

How it works

StreamHtml runs a repair pipeline on every chunk:

  1. Strip incomplete tags<div class="met → removed until the tag completes
  2. Close open tags<strong>bold<strong>bold</strong>
  3. Split blocks — Completed top-level blocks are frozen; only the tail re-renders
  4. Patch incrementally — Plain text and table rows append to the live DOM
  5. Sanitize — DOMPurify removes XSS vectors before dangerouslySetInnerHTML
Streaming input          Repair                 Render
─────────────────       ──────────             ──────────
<div>A</div><div>B      stable: [<div>A</div>]  memoized ✓
<div>C                  live: <div>C</div>      re-renders each chunk

API

<StreamHtml />

| Prop | Type | Default | Description | |------|------|---------|-------------| | children | string | "" | HTML content to render | | reasoning | string | "" | Plain-text reasoning / thinking tokens | | isStreaming | boolean | false | Shows caret, marks streaming state | | caret | boolean | isStreaming | Toggle streaming caret | | repair | boolean | true | Run incomplete HTML repair | | sanitize | boolean | true | DOMPurify sanitization | | memoizeBlocks | boolean | true | Freeze completed blocks | | sanitizeConfig | Config | — | DOMPurify config override | | thinkingLabel | string | "Thinking" | Label for reasoning panel | | className | string | — | Root element class |

rehtml(input, options?)

Headless repair function for non-React use:

import { rehtml } from "@alphanimble/streamhtml";

const { html, stable, live, hadIncompleteTag } = rehtml(partialHtml);

sanitizeHtml(html, config?)

import { sanitizeHtml, configureSanitizer } from "@alphanimble/streamhtml";

configureSanitizer({ ALLOWED_TAGS: ["div", "p", "span", "table", ...] });
const safe = sanitizeHtml(untrustedHtml);

Prompting tips

Tell your model to output semantic HTML with CSS classes:

<div class="metrics">
  <div class="metric good">
    <div class="metric-val">142ms</div>
    <div class="metric-lbl">avg latency</div>
  </div>
</div>
<table class="rt">
  <thead><tr><th>Endpoint</th><th>Status</th></tr></thead>
  <tbody>...</tbody>
</table>
<div class="callout warn"><strong>Note:</strong> ...</div>

Define component styles in your app — StreamHtml ships minimal base styles for tables, code, and typography.

Development

git clone <your-repo-url>
cd htmlstream
npm install
npm test
npm run build

Demos

Gallery (static examples):

npm run demo

Chat (live OpenRouter streaming):

cp .env.example .env
# Add your OPENROUTER_API_KEY and optional MODEL_NAME
npm run chat

The chat demo streams HTML from OpenRouter and persists sessions locally in .chat-sessions/ (gitignored).

License

MIT — see LICENSE.