用途
TanStack Markdown 是一个框架中立的 Markdown 解析与渲染工具,提供窄入口的解析器与 HTML 渲染模块,可用于受控的博客和文档内容。它并非完整的 CommonMark、GFM、MDX 或任意 HTML 解析器,仅针对受控内容设计。
安装
使用 pnpm 安装框架中立包:
pnpm add @tanstack/markdown
当默认入口的合并导出不必要时,可使用更窄的 @tanstack/markdown/parser 和 @tanstack/markdown/html 入口点。
主要能力
渲染受支持的 Markdown 到 HTML:
import { renderHtml } from '@tanstack/markdown/html'
const source = `# Release notes
Small browser bundle
Serializable AST
Safe HTML defaults`
const html = renderHtml(source)
console.log(html)
解析一次并渲染可复用文档
import { renderHtml } from '@tanstack/markdown/html'
import { parseMarkdown } from '@tanstack/markdown/parser'
import type { MarkdownDocument } from '@tanstack/markdown'
const source = `# Guide
Read the [installation notes](/installation).`
const document = parseMarkdown(source)
const serialized = JSON.stringify(document)
const restored = JSON.parse(serialized) as MarkdownDocument
const html = renderHtml(restored)
console.log(html)
所有完整文档渲染器都接受源字符串或已有的 MarkdownDocument;传入文档输入可跳过解析。
解析时配置 frontmatter 和标题 ID
import { renderHtml } from '@tanstack/markdown/html'
import { parseMarkdown } from '@tanstack/markdown/parser'
const source = `---
title: Installation
published: true
---
# Install
## Install`
const document = parseMarkdown(source, {
frontmatter: true,
headingIds(text, lineIndex) {
const slug = text.toLowerCase().replaceAll(' ', '-')
return `docs-${lineIndex}-${slug}`
},
})
console.log(document.frontmatter)
console.log(renderHtml(document))
Frontmatter 保持为未解析的字符串。标题 ID 在解析期间生成,并在默认 slugger 下具备防重复能力。
渲染时添加可见的标题锚点
import { renderHtml } from '@tanstack/markdown/html'
import { parseMarkdown } from '@tanstack/markdown/parser'
const document = parseMarkdown('# API')
const html = renderHtml(document, {
headingAnchors: {
content: '#',
className: 'heading-anchor',
ariaHidden: true,
tabIndex: -1,
},
})
console.log(html)
标题 ID 是解析关注点;可见锚点链接是渲染关注点。
仅在不需要块上下文时解析内联内容
import { parseInline, renderInline } from '@tanstack/markdown'
const nodes = parseInline('Use **stable** APIs and `parseMarkdown`.')
const html = nodes.map(node => renderInline(node)).join('')
console.log(html)
parseInline 仅处理内联语法。标题、列表、表格、frontmatter、引用定义和脚注定义需使用 parseMarkdown。
行为契约
- 解析是同步、确定性的,并会规范化行尾。
- 仅在 allowHtml: true 时识别原始块级和内联 HTML。
- 链接和图片 URL 中不安全的可执行协议会被移除。
- 紧凑列表将简单内容直接放在 <li> 下;宽松列表保留段落包装;任务复选框保持与标签内联。
- 引用定义在块解析前以不区分大小写的方式解析。
- 脚注按首次引用顺序渲染,具有防冲突 ID 和重复引用回链。
- 代码围栏元数据记录在 AST 中;高亮为外部功能。
- AST 是公开的但处于 pre-1.0 阶段。当升级改变节点契约时需重新生成持久化文档。
兼容性与选项时机
TanStack Markdown 针对受控的博客和文档内容,而非完整的 CommonMark、GFM、MDX 或任意 HTML 解析。添加语法前需有语料库证据、回归覆盖、渲染器一致性以及可接受的包体积成本。生产管线需参见 production-pipelines/SKILL.md 中的兼容性、安全性、高亮和体积门禁。
解析会固定文档结构。frontmatter、headingIds、allowHtml、解析器状态以及解析/转换扩展需在缓存 AST 之前应用。仅渲染器选项如 headingAnchors、highlighter 和 codeLineNumbers 可在渲染已有文档时应用。
常见错误
HIGH 假设完整的 CommonMark 或 GFM 行为
错误:
import { renderHtml } from '@tanstack/markdown/html'
const markdownFromAnywhere = `Heading
===
Visit https://example.com`
const html = renderHtml(markdownFromAnywhere)
console.log(html)
正确:
import { renderHtml } from '@tanstack/markdown/html'
const controlledMarkdown = `# Heading
Visit [example](https://example.com)`
const html = renderHtml(controlledMarkdown)
console.log(html)
Setext 标题和自动 URL 链接不在支持的语法配置范围内,因此不支持的输入可能保持字面形式或产生不同结构。
MEDIUM 每次渲染都重新解析未更改的内容
错误:
每次调用完整文档渲染器时都会解析字符串输入,而传入 MarkdownDocument 输入可跳过重复解析。应解析一次并复用文档对象。
HIGH 在解析之后应用解析器选项
错误:
仅解析选项不会追溯性地修改已预解析的 AST。应将 headingIds 等解析选项传给 parseMarkdown,而非 renderHtml。
MEDIUM 使用 parseInline 处理文档定义
错误:
独立的内联解析不会提取文档级引用或脚注定义,除非显式提供内部解析器状态。应使用 parseMarkdown 处理此类内容。
相关技能
- production-pipelines/SKILL.md - 信任边界、语法高亮、实用兼容性、缓存、测试、性能和包体积预算。
- react-rendering/SKILL.md - 将同一源或 AST 渲染为 React 节点。
- octane-rendering/SKILL.md - 将同一源或 AST 渲染为 Octane 节点。
- custom-extensions/SKILL.md - 添加确定性的解析器和渲染器钩子。
- docs-features/SKILL.md - 使用第一方文档扩展。
参考资料
AST 节点、解析器状态和渲染选项。