Markdown

Markdown für KI-Antworten. Es streamt flüssig und zeigt nie halb getippte Syntax, mit verlinkbaren Überschriften, Tabellen, die du als Markdown oder CSV kopieren kannst, GitHub-Callouts, Aufgabenlisten, Fußnoten, Bildern, Inline-Zitaten und vollständigen Codeblöcken.

Modelle antworten in Markdown und schreiben es Token für Token. Ein einfacher Renderer zeigt beim Streamen rohe Sternchen, halb gebaute Tabellen und ungeschlossene Code-Fences kurz an. Markdown vervollständigt unfertige Syntax unterwegs und taktet den Text wie der Streaming-Block, sodass Formatierung an Ort und Stelle erscheint.

Jedes Element ist aus dem Rest von HextaUI gebaut. Code-Fences werden zu Code Blocks, GitHub-Callouts wie [!NOTE] nutzen Alert, Aufgabenlisten nutzen schreibgeschützte Checkboxen, und Tabellen scrollen seitwärts, mit einem Menü, um sie als Markdown oder CSV zu kopieren. Übergib sources, und [1] wird zu einem Zitat-Chip, während Markierungen in Code unangetastet bleiben.

Überschriften erhalten IDs aus ihrem Text, mit optionalem Link-Icon zum Teilen. Bilder laden lazy mit Platzhalter und Bildunterschrift, Fußnoten verlinken zurück zu ihrer Markierung, externe Links öffnen in einem neuen Tab und sagen das, und die Textrichtung wird pro Block erkannt. Es funktioniert für READMEs und Changelogs ebenso gut wie für gestreamte Antworten.

  1. Die Pro-Registry zu components.json hinzufügen

    components.json
    {
      "registries": {
        "@hextaui-pro": {
          "url": "https://hextaui.com/r/pro/{name}.json",
          "headers": {
            "Authorization": "Bearer ${HEXTAUI_PRO_TOKEN}"
          }
        }
      }
    }
  2. Token hinzufügen

    Erstelle auf deiner Kontoseite einen Token und trage ihn in .env.local als HEXTAUI_PRO_TOKEN ein.

  3. Den Block hinzufügen

    pnpm dlx shadcn@latest add @hextaui-pro/markdown

Mit dem AI SDK

Rendere jeden Text-Part einer useChat-Nachricht, mit source-url-Parts als Quellen. Der Zustand des Parts sagt Markdown, wann das Modell noch schreibt.

"use client"

import type { UIMessage } from "ai"

import { Markdown } from "@/components/blocks/markdown/markdown"

export function AssistantMessage({ message }: { message: UIMessage }) {
  const sources = message.parts.flatMap((part) =>
    part.type === "source-url"
      ? [{ id: part.sourceId, url: part.url, title: part.title ?? part.url }]
      : []
  )

  return message.parts.map((part, index) =>
    part.type === "text" ? (
      <Markdown
        key={index}
        text={part.text}
        streaming={part.state === "streaming"}
        sources={sources}
      />
    ) : null
  )
}

Statischer Inhalt

Übergib einen String als children für READMEs, Changelogs oder gespeicherte Antworten. Setze anchors, um Überschriften einen teilbaren Link zu geben.

import { Markdown } from "@/components/blocks/markdown/markdown"

const changelog = `## 2.4.0

> [!NOTE]
> Requires React 19.

- [x] Faster cold starts
- [x] Copy tables as CSV
- [ ] Offline mode`

export function Changelog() {
  return <Markdown anchors size="base">{changelog}</Markdown>
}

Aufbau

Die Teile, die du zusammensetzt, von außen nach innen.

PartBeschreibung
MarkdownDer Renderer. Nimmt einen String und übernimmt Takt, Parsing und jedes Element.
MarkdownTableEine scrollbare Tabelle mit dem Kopieren-Menü.
MarkdownAlertEin Callout für [!NOTE] und die anderen GitHub-Alert-Typen.
MarkdownHeadingEine Überschrift mit id und optionalem Anker-Link.
MarkdownImageEin Bild mit Lade-, Fehler- und Bildunterschrift-Zuständen.
PropTypStandard
textDas bisherige Markdown. Du kannst es stattdessen als children übergeben.
string–
streamingTaktet den Text, zeigt den Cursor und vervollständigt unfertige Syntax.
booleanfalse
smoothSchalte das Taktgeben ab, um Text genau so zu rendern, wie er eintrifft.
booleantrue
sourcesMacht aus [1] einen Chip für sources[0] und so weiter. Ohne sources bleibt [1] einfacher Text.
Source[][]
sizeTextgröße. Nutze sm im Chat und base für Artikel.
"sm" | "base""sm"
anchorsZeigt ein Link-Icon an Überschriften.
booleanfalse
componentsÜberschreibe ein beliebiges Element, etwa a oder img. Halte das Objekt stabil, zum Beispiel außerhalb der Komponente, damit Blocks nicht neu gemountet werden.
Components–
classNameKlassen für den Wrapper.
string–
TasteAktion
TabWechselt in Lesereihenfolge durch Links, Zitate, Überschriften-Anker, Tabellen-Kopiermenüs und Codeblock-Aktionen.
EnterÖffnet einen Link oder das Kopiermenü einer Tabelle.
↑↓Wechselt zwischen Copy as Markdown und Copy as CSV.
  • Der Wrapper ist beim Streamen mit aria-busy markiert, sodass Screenreader warten, bis sich der Text setzt.
  • Callouts nutzen role=note mit ihrem Typ als Label, statt wie Live-Alerts anzusagen.
  • Aufgabenlisten-Checkboxen sind schreibgeschützt und werden von Tab übersprungen, sodass sie als Zustand statt als Steuerelemente gelesen werden.
  • Externe Links sagen „öffnet in einem neuen Tab“, Zitat-Chips tragen den Namen ihrer Quelle, und Überschriften-Anker heißen „Link zu …“.
  • Bilder behalten ihren Alt-Text, und Tabellen behalten ihre Kopfzellen für die Screenreader-Navigation.
  • Bei reduzierter Bewegung hört der Cursor auf zu pulsieren, und Bilder erscheinen ohne Einblenden.

Gebaut mit

Die kostenlosen HextaUI-Komponenten, aus denen Markdown besteht. Jede lässt sich einzeln installieren.

Code

3 Dateien, hinzugefügt zu components/blocks/markdown.