Markdown

Markdown hecho para respuestas de IA. Se transmite con fluidez y nunca muestra sintaxis a medio escribir, con encabezados enlazables, tablas que puedes copiar como Markdown o CSV, callouts de GitHub, listas de tareas, notas al pie, imágenes, citas en línea y bloques de código completos.

Los modelos responden en Markdown y lo escriben token a token. Un renderizador simple muestra asteriscos sin procesar, tablas a medio construir y bloques de código sin cerrar mientras llega. Markdown completa la sintaxis sin terminar sobre la marcha y regula el ritmo del texto como el bloque Streaming, de modo que el formato aparece en su sitio.

Cada elemento se construye con el resto de HextaUI. Los bloques de código delimitados se convierten en Code Blocks, los callouts de GitHub como [!NOTE] usan Alert, las listas de tareas usan Checkboxes de solo lectura, y las tablas se desplazan lateralmente con un menú para copiarlas como Markdown o CSV. Pasa sources y [1] se convierte en un chip de cita, mientras que las marcas dentro del código se dejan intactas.

Los encabezados obtienen ids a partir de su texto, con un icono de enlace opcional para compartirlos. Las imágenes se cargan de forma diferida con un marcador y pie de foto, las notas al pie enlazan de vuelta a su marca, los enlaces externos se abren en una pestaña nueva y lo indican, y la dirección del texto se detecta por bloque. Funciona igual de bien para READMEs y registros de cambios que para respuestas en streaming.

  1. Añade el registro Pro a components.json

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

    Crea un token en tu página de cuenta y colócalo en .env.local como HEXTAUI_PRO_TOKEN.

  3. Añade el bloque

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

Con AI SDK

Renderiza cada parte de texto de un mensaje de useChat, con las partes source-url como sus fuentes. El estado de la parte le indica a Markdown cuándo el modelo sigue escribiendo.

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

Contenido estático

Pasa una cadena como children para READMEs, registros de cambios o respuestas guardadas. Establece anchors para dar a los encabezados un enlace que se pueda compartir.

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

Anatomía

Las partes que compones, de fuera hacia dentro.

ParteDescripción
MarkdownEl renderizador. Toma una cadena y se encarga del ritmo, el análisis y cada elemento.
MarkdownTableUna tabla desplazable con el menú de copia.
MarkdownAlertUn callout para [!NOTE] y los demás tipos de alerta de GitHub.
MarkdownHeadingUn encabezado con un id y un enlace ancla opcional.
MarkdownImageUna imagen con estados de carga, error y pie de foto.
PropTipoPredeterminado
textEl Markdown hasta ahora. Puedes pasarlo como children en su lugar.
string–
streamingRegula el ritmo del texto, muestra el cursor y completa la sintaxis sin terminar.
booleanfalse
smoothDesactiva el ritmo para renderizar el texto exactamente como se recibe.
booleantrue
sourcesConvierte [1] en un chip para sources[0], y así sucesivamente. Sin sources, [1] permanece como texto plano.
Source[][]
sizeTamaño del texto. Usa sm en el chat y base para artículos.
"sm" | "base""sm"
anchorsMuestra un icono de enlace en los encabezados.
booleanfalse
componentsSustituye cualquier elemento, como a o img. Mantén el objeto estable, por ejemplo fuera del componente, para que los bloques no se vuelvan a montar.
Components–
classNameClases para el contenedor.
string–
KeyAcción
TabRecorre enlaces, citas, anclas de encabezado, menús de copia de tablas y acciones de bloques de código en orden de lectura.
EnterAbre un enlace, o el menú de copia de una tabla.
↑↓Alterna entre Copy as Markdown y Copy as CSV.
  • El contenedor se marca con aria-busy mientras llega, para que los lectores de pantalla esperen a que el texto se asiente.
  • Los callouts usan role=note con su tipo como etiqueta, en lugar de anunciarse como alertas live.
  • Las casillas de las listas de tareas son de solo lectura y Tab las omite, así que se leen como estado y no como controles.
  • Los enlaces externos dicen «opens in a new tab», los chips de cita se nombran con su fuente y las anclas de encabezado se llaman «Link to …».
  • Las imágenes conservan su texto alt y las tablas conservan sus celdas de encabezado para la navegación con lector de pantalla.
  • Con movimiento reducido, el cursor deja de pulsar y las imágenes aparecen sin fundido.

Construido con

Los componentes gratuitos de HextaUI con los que está hecho Markdown. Cada uno se instala por separado.

Código

3 archivos, añadidos a components/blocks/markdown.