Markdown

Markdown feito para respostas de IA. Ele faz streaming com suavidade e nunca mostra sintaxe pela metade, com títulos para os quais você pode criar links, tabelas que você pode copiar como Markdown ou CSV, callouts do GitHub, listas de tarefas, notas de rodapé, imagens, citações inline e blocos de código completos.

Os modelos respondem em Markdown e o escrevem um token por vez. Um renderizador simples exibe asteriscos crus, tabelas pela metade e blocos de código sem fechamento enquanto o texto chega. O Markdown completa a sintaxe inacabada à medida que avança e dá ritmo ao texto como o bloco Streaming, de modo que a formatação aparece no lugar.

Cada elemento é construído com o restante do HextaUI. Os blocos de código viram Code Blocks, os callouts do GitHub como [!NOTE] usam o Alert, as listas de tarefas usam Checkboxes somente leitura, e as tabelas rolam para os lados com um menu para copiá-las como Markdown ou CSV. Passe sources e [1] vira um chip de citação, enquanto marcas dentro de código são deixadas como estão.

Os títulos recebem ids a partir do texto, com um ícone de link opcional para compartilhá-los. As imagens carregam sob demanda com placeholder e legenda, as notas de rodapé levam de volta à sua marca, os links externos abrem em uma nova aba e informam isso, e a direção do texto é detectada por bloco. Funciona tão bem para READMEs e changelogs quanto para respostas em streaming.

  1. Adicione o registro Pro ao components.json

    components.json
    {
      "registries": {
        "@hextaui-pro": {
          "url": "https://hextaui.com/r/pro/{name}.json",
          "headers": {
            "Authorization": "Bearer ${HEXTAUI_PRO_TOKEN}"
          }
        }
      }
    }
  2. Adicione seu token

    Crie um token na sua página de conta e coloque-o em .env.local como HEXTAUI_PRO_TOKEN.

  3. Adicione o bloco

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

Com o AI SDK

Renderize cada parte de texto de uma mensagem do useChat, com as partes source-url como suas fontes. O state da parte informa ao Markdown quando o modelo ainda está escrevendo.

"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
  )
}

Conteúdo estático

Passe uma string como children para READMEs, changelogs ou respostas salvas. Defina anchors para dar aos títulos um link que as pessoas possam compartilhar.

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>
}

Anatomia

As partes que você compõe, de fora para dentro.

ParteDescrição
MarkdownO renderizador. Recebe uma string e cuida do ritmo, da análise e de cada elemento.
MarkdownTableUma tabela rolável com o menu de cópia.
MarkdownAlertUm callout para [!NOTE] e os outros tipos de alerta do GitHub.
MarkdownHeadingUm título com id e link de âncora opcional.
MarkdownImageUma imagem com estados de carregamento, erro e legenda.
PropTipoPadrão
textO Markdown até agora. Você pode passá-lo como children no lugar.
string–
streamingDá ritmo ao texto, mostra o cursor e completa a sintaxe inacabada.
booleanfalse
smoothDesative o ritmo para renderizar o texto exatamente como recebido.
booleantrue
sourcesTransforma [1] em um chip para sources[0], e assim por diante. Sem sources, [1] permanece como texto simples.
Source[][]
sizeTamanho do texto. Use sm no chat e base em artigos.
"sm" | "base""sm"
anchorsMostra um ícone de link nos títulos.
booleanfalse
componentsSubstitua qualquer elemento, como a ou img. Mantenha o objeto estável, por exemplo fora do componente, para que os blocos não sejam remontados.
Components–
classNameClasses para o wrapper.
string–
TeclaAção
TabPercorre links, citações, âncoras de título, menus de cópia de tabelas e ações de blocos de código na ordem de leitura.
EnterAbre um link, ou o menu de cópia de uma tabela.
↑↓Alterna entre Copy as Markdown e Copy as CSV.
  • O wrapper é marcado com aria-busy enquanto o conteúdo chega em streaming, para que os leitores de tela esperem o texto se estabilizar.
  • Os callouts usam role=note com o seu tipo como rótulo, em vez de serem anunciados como alertas live.
  • As caixas de seleção das listas de tarefas são somente leitura e ignoradas pelo Tab, então são lidas como estado, e não como controles.
  • Os links externos dizem "abre em uma nova aba", os chips de citação têm o nome de sua fonte e as âncoras de título se chamam "Link para …".
  • As imagens mantêm o texto alternativo, e as tabelas mantêm as células de cabeçalho para a navegação por leitor de tela.
  • Com movimento reduzido, o cursor para de pulsar e as imagens aparecem sem fade.

Construído com

Os componentes gratuitos do HextaUI de que Markdown é feito. Cada um é instalado separadamente.

Código

3 arquivos, adicionados a components/blocks/markdown.