DeltaKitDeltaKit

Markdown

Lightweight React component for rendering complete (non-streaming) markdown.

Markdown renders markdown content without any streaming overhead. Use it for historical messages, static content, or any case where the content is already complete.

import { Markdown } from "@deltakit/markdown";

When to Use

In a chat UI, most messages in the transcript are already complete — only the latest assistant message is actively streaming. Using StreamingMarkdown for every historical message adds unnecessary overhead:

  • Batching/debouncing timers (batchMs)
  • React.memo block wrapper with streaming opacity transitions
  • Per-render state management (useState, useEffect)

Markdown skips all of this. It parses once with useMemo and renders clean semantic HTML.

{messages.map((msg) => {
  const isStreaming = msg.id === activeMessageId && msg.role === "assistant";
  const Component = isStreaming ? StreamingMarkdown : Markdown;
  return (
    <div key={msg.id}>
      {msg.parts
        .filter((p) => p.type === "text")
        .map((p, i) => (
          <Component key={i} content={p.text} />
        ))}
    </div>
  );
})}

Props

content (required)

Type: string

The complete markdown string to render.

<Markdown content="# Hello World\n\nThis is **bold** text." />

components

Type: ComponentOverrides

Override renderers for specific HTML elements. Same API as StreamingMarkdown. See Custom Components for details.

<Markdown
  content={text}
  components={{
    code({ language, children, inline }) {
      if (inline) return <code>{children}</code>;
      return <pre><code>{children}</code></pre>;
    },
  }}
/>

className

Type: string

CSS class applied to the wrapper <div>.

<Markdown content={text} className="prose dark:prose-invert" />

Differences from StreamingMarkdown

BehaviorStreamingMarkdownMarkdown
Render batching (batchMs)Yes (configurable)No
Incomplete syntax bufferingYes (configurable)No (bufferIncomplete: false)
Streaming opacity transitionsYesNo
Shared style injectionYesNo (no styles needed)
React.memo block wrapperYesNo
useState / useEffectYes (3 hooks)No (only useMemo)

Both components accept the same components and className props, and produce identical DOM output for the same content.

On this page