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

markstream-react

v0.0.53

Published

React/Next.js streaming Markdown renderer for AI chat, LLM token streams, SSE/WebSocket output, incomplete Markdown, long documents, Mermaid, KaTeX, Shiki, Monaco, and custom React components.

Readme

markstream-react

React/Next.js streaming Markdown renderer for AI chat, SSE/WebSocket output, long AI responses, Mermaid, KaTeX, and code blocks.

markstream-react is the React renderer in the Markstream family. It renders raw Markdown strings with content, and it can also accept pre-parsed nodes when a worker or store already owns parsing.

Install

pnpm add markstream-react

Optional features are peer dependencies. Install only what your Markdown output needs.

Quick Start

Import one Markstream CSS file explicitly. The JavaScript entry does not inject styles automatically.

import MarkdownRender from 'markstream-react'
import 'markstream-react/index.css'

export default function ChatMessage({
  content,
  isDone,
}: {
  content: string
  isDone: boolean
}) {
  return <MarkdownRender content={content} final={isDone} fade={false} />
}

Use markstream-react/index.px.css instead when your app scales the root font size on mobile and you want renderer sizing to stay pixel-based.

Streaming Example

For most SSE/WebSocket chat surfaces, accumulate the Markdown string and pass content plus final:

import MarkdownRender from 'markstream-react'
import { useEffect, useState } from 'react'
import 'markstream-react/index.css'

export function ChatView() {
  const [content, setContent] = useState('')
  const [isDone, setIsDone] = useState(false)

  useEffect(() => {
    const eventSource = new EventSource('/api/chat/stream')
    eventSource.onmessage = (event) => {
      if (event.data === '[DONE]') {
        setIsDone(true)
        eventSource.close()
        return
      }

      const data = JSON.parse(event.data) as { content?: string }
      setContent(prev => prev + (data.content ?? ''))
    }

    return () => eventSource.close()
  }, [])

  return <MarkdownRender content={content} final={isDone} fade={false} />
}

If parsing is already external, pass nodes. Use a per-message parser id so generated code-block DOM ids stay unique across chat lists.

import MarkdownRender from 'markstream-react'
import { useMemo } from 'react'
import { getMarkdown, parseMarkdownToStructure } from 'stream-markdown-parser'

export function ParsedChatMessage({
  messageId,
  content,
  isDone,
}: {
  messageId: string
  content: string
  isDone: boolean
}) {
  const md = useMemo(() => getMarkdown(`chat-${messageId}`), [messageId])
  const nodes = useMemo(
    () => parseMarkdownToStructure(content, md, { final: isDone }),
    [content, isDone, md],
  )

  return <MarkdownRender nodes={nodes} final={isDone} fade={false} />
}

Next.js SSR

Import styles once from your app shell:

// app/layout.tsx or pages/_app.tsx
import 'markstream-react/index.css'

Use the root package in client components for live SSE/WebSocket streams:

'use client'

import MarkdownRender from 'markstream-react'

export function LiveMessage({ content, isDone }: { content: string, isDone: boolean }) {
  return <MarkdownRender content={content} final={isDone} fade={false} />
}

Use markstream-react/next for SSR-first Markdown with client enhancement, or markstream-react/server for server-only rendering:

import MarkdownRender from 'markstream-react/next'

export default function Page() {
  return <MarkdownRender content="# Server HTML first" final />
}

Optional Peers

| Feature | Package | | --- | --- | | Shiki code blocks | stream-markdown | | Monaco editor code blocks | stream-monaco | | Mermaid diagrams | mermaid | | KaTeX math | katex | | D2 diagrams | @terrastruct/d2 | | Infographic blocks | @antv/infographic |

KaTeX still needs its CSS in your app when math rendering is enabled:

import 'katex/dist/katex.min.css'

Tailwind

Non-Tailwind projects should import the precompiled CSS:

import 'markstream-react/index.css'

Tailwind projects can import the Tailwind-ready CSS and include the extracted class list in tailwind.config.js:

import 'markstream-react/index.tailwind.css'
module.exports = {
  content: [
    './src/**/*.{js,ts,jsx,tsx}',
    require('markstream-react/tailwind'),
  ],
}

Custom Components

For HTML-like custom tags in new React code, prefer renderer-local component maps:

  • streamingComponents receives parser-backed NodeComponentProps, including node.attrs, node.content, and node.loading.
  • htmlComponents renders through the raw/dynamic HTML path and receives normal React props plus children.
import type { NodeComponentProps } from 'markstream-react'
import type React from 'react'
import MarkdownRender from 'markstream-react'

function DocumentLink(props: NodeComponentProps<{ type: 'documentlink', content: string, loading?: boolean }>) {
  return <span aria-busy={props.node.loading || undefined}>{props.node.content}</span>
}

function Badge({ kind, children }: React.PropsWithChildren<{ kind?: string }>) {
  return <span data-kind={kind}>{children}</span>
}

const renderer = (
  <MarkdownRender
    content={content}
    final={isDone}
    streamingComponents={{ documentlink: DocumentLink }}
    htmlComponents={{ badge: Badge }}
  />
)

streamingComponents keys are normalized and automatically added to the parser's effective customHtmlTags, so incomplete tags can render while content is streaming.

customHtmlTags remains available as a lower-level parser option. setCustomComponents and customId also remain supported for compatibility, shared application-level registration, and existing node overrides:

import MarkdownRender, { setCustomComponents } from 'markstream-react'

setCustomComponents('chat', {
  documentlink: DocumentLink,
})

const legacyRenderer = (
  <MarkdownRender
    customId="chat"
    customHtmlTags={['documentlink']}
    content={content}
  />
)

Without customHtmlTags or streamingComponents, registered tag components render through the raw HTML path and receive HTML-style props/children instead of props.node. HTML safety is still handled by htmlPolicy and sanitization; the API split is not a security boundary.

When Not to Use It

Use react-markdown, marked, or markdown-it when you only render short static Markdown, need the smallest possible Markdown stack, or already have a complete remark/rehype pipeline and do not need streaming mid-state handling.

Type Exports

The package root exports the public component and renderer types, including NodeRendererProps, NodeComponentProps, StreamingComponentMap, HtmlComponentMap, RenderContext, RenderNodeFn, CustomComponentMap, and code-block option types.

Development

pnpm --filter markstream-react dev
pnpm --filter markstream-react build
pnpm --filter markstream-react check:exports
pnpm --filter markstream-react size:check