Agent SkillsTanStack/markdown › docs-features

docs-features

GitHub

构建文档元数据,支持GitHub风格提示、标题收集及多种Tab组件(文件/包管理器/框架),用于解析和渲染TanStack风格的文档内容。

skills/docs-features/SKILL.md TanStack/markdown

Trigger Scenarios

需要解析Markdown文档并提取结构化元数据 在文档中实现多标签页切换功能 处理带有特定语法的文档内容

Install

npx skills add TanStack/markdown --skill docs-features -g -y
More Options

Use without installing

npx skills use TanStack/markdown@docs-features

指定 Agent (Claude Code)

npx skills add TanStack/markdown --skill docs-features -a claude-code -g -y

安装 repo 全部 skill

npx skills add TanStack/markdown --all -g -y

预览 repo 内 skill

npx skills add TanStack/markdown --list

SKILL.md

Frontmatter
{
    "name": "docs-features",
    "sources": [
        "TanStack\/markdown:docs\/guides\/docs-preset.md",
        "TanStack\/markdown:docs\/reference\/extensions.md",
        "TanStack\/markdown:src\/extensions\/docs.ts",
        "TanStack\/markdown:src\/extensions\/tabs.ts",
        "TanStack\/markdown:src\/extensions\/framework.ts",
        "TanStack\/markdown:src\/extensions\/headings.ts",
        "TanStack\/markdown:src\/extensions\/comment-components.ts"
    ],
    "metadata": {
        "type": "core",
        "library": "@tanstack\/markdown",
        "library_version": "0.0.15"
    },
    "requires": [
        "render-markdown"
    ],
    "description": "Build documentation metadata with docsMarkdownExtensions, GitHub-style callouts, heading collection, comment components, heading\/file\/package- manager\/bundler tabs, framework panels, and code-fence metadata. Load when authoring or consuming TanStack-style docs syntax and custom-element data contracts.\n"
}

This skill builds on render-markdown. Read it first for parser options, AST reuse, rendering, and core trust boundaries.

Docs Features

Setup

Create one extension array and use it for parsing and rendering:

import { renderHtml } from '@tanstack/markdown/html'
import { parseMarkdown } from '@tanstack/markdown/parser'
import { docsMarkdownExtensions } from '@tanstack/markdown/extensions/docs'

const source = `# Deployment

> [!TIP] Parse once
> Reuse the document for every renderer.

## Install

Run the package-manager command for your application.`

const extensions = docsMarkdownExtensions()
const document = parseMarkdown(source, { extensions })

export const headings = document.headings
export const html = renderHtml(document, {
  extensions,
  headingAnchors: true,
})

The preset composes callouts, transformed comment components, and heading collection. It emits metadata and custom elements; it does not install documentation UI behavior.

Core Patterns

Collect headings without selector headings

import { parseMarkdown } from '@tanstack/markdown/parser'
import { docsMarkdownExtensions } from '@tanstack/markdown/extensions/docs'

const source = `# Guide

<!-- ::start:tabs -->

## React

React setup

## Solid

Solid setup

<!-- ::end:tabs -->

## API`

const extensions = docsMarkdownExtensions()

export const document = parseMarkdown(source, { extensions })
export const headings = document.headings

Heading collection defaults to skipping every heading inside a component named tabs. Pass docsMarkdownExtensions({ collectHeadings: false }) to disable collection or an options object with skipComponentNames to replace the default skip set.

Author each tab input shape

Heading tabs split at the shallowest heading:

<!-- ::start:tabs -->

## React

React setup

## Solid

Solid setup

<!-- ::end:tabs -->

File tabs select fenced code blocks and use title= or file= as labels:

<!-- ::start:tabs variant="files" -->

```tsx file="app.tsx"
export function App() {
  return <main>Docs</main>
}
```

```css file="app.css"
main {
  display: block;
}
```

<!-- ::end:tabs -->

Package-manager tabs consume framework: package... lines and remove their source children after creating metadata:

<!-- ::start:tabs variant="package-manager" mode="dev-install" -->

react: @tanstack/react-query @tanstack/react-router
solid: @tanstack/solid-query @tanstack/solid-router

<!-- ::end:tabs -->

Bundler tabs accept only Vite and Rsbuild heading sections:

<!-- ::start:tabs variant="bundler" -->

## Vite

```ts
export default { plugins: [] }
```

## Rsbuild

```ts
export default { plugins: [] }
```

<!-- ::end:tabs -->

Every transform returns the original component unchanged when its required structure is absent. See docs metadata contracts for exact properties and fallback rules.

Build framework-specific panels

<!-- ::start:framework -->

# React

## Install

```tsx title="react.tsx"
export const framework = 'react'

Solid

Install

export const framework = 'solid'

Framework blocks require level-one selector headings. They emit `md-framework-panel` children, lowercase framework names, label nested headings, and expose code-block metadata by framework.

### Read component metadata before rendering

```ts
import type { ComponentNode } from '@tanstack/markdown'
import { parseMarkdown } from '@tanstack/markdown/parser'
import { docsMarkdownExtensions } from '@tanstack/markdown/extensions/docs'

interface TabDescriptor {
  slug: string
  name: string
}

function isTabsNode(node: unknown): node is ComponentNode {
  return (
    typeof node === 'object' &&
    node !== null &&
    'type' in node &&
    node.type === 'component' &&
    'name' in node &&
    node.name === 'tabs'
  )
}

const source = `<!-- ::start:tabs -->

## React

React content

## Solid

Solid content

<!-- ::end:tabs -->`

const document = parseMarkdown(source, {
  extensions: docsMarkdownExtensions(),
})
const tabsNode = document.children.find(isTabsNode)
const metadata = tabsNode?.properties?.['data-attributes']

export const tabs: TabDescriptor[] = metadata
  ? (JSON.parse(metadata) as { tabs: TabDescriptor[] }).tabs
  : []

ComponentNode.properties holds strings. JSON-bearing values are serialized into escaped HTML attributes by the HTML renderer and must be parsed by the consuming application.

Common Mistakes

HIGH Assuming transformed tabs are interactive

Wrong:

import { renderHtml } from '@tanstack/markdown/html'
import { docsMarkdownExtensions } from '@tanstack/markdown/extensions/docs'

const source = `<!-- ::start:tabs -->

## React

React content

<!-- ::end:tabs -->`

export const interactiveTabs = renderHtml(source, {
  extensions: docsMarkdownExtensions(),
})

Correct:

import type { ComponentNode } from '@tanstack/markdown'
import { parseMarkdown } from '@tanstack/markdown/parser'
import { docsMarkdownExtensions } from '@tanstack/markdown/extensions/docs'

const source = `<!-- ::start:tabs -->

## React

React content

<!-- ::end:tabs -->`

const document = parseMarkdown(source, {
  extensions: docsMarkdownExtensions(),
})
const tabsNode = document.children.find(
  (node): node is ComponentNode =>
    node.type === 'component' && node.name === 'tabs',
)

export const tabPanels =
  tabsNode?.children.filter(
    (node): node is ComponentNode =>
      node.type === 'component' && node.tagName === 'md-tab-panel',
  ) ?? []

The transform supplies a model and custom-element names; the site must bind those values to its own state, controls, and renderer components.

Source: docs/guides/docs-preset.md

HIGH Leaving a component block unmatched

Wrong:

import { parseMarkdown } from '@tanstack/markdown/parser'
import { docsMarkdownExtensions } from '@tanstack/markdown/extensions/docs'

const source = `<!-- ::start:tabs -->

## React

React content`

export const document = parseMarkdown(source, {
  extensions: docsMarkdownExtensions(),
})

Correct:

import { parseMarkdown } from '@tanstack/markdown/parser'
import { docsMarkdownExtensions } from '@tanstack/markdown/extensions/docs'

const source = `<!-- ::start:tabs -->

## React

React content

<!-- ::end:tabs -->`

export const document = parseMarkdown(source, {
  extensions: docsMarkdownExtensions(),
})

An unmatched start is consumed as an empty component, while the following body remains ordinary Markdown.

Source: src/extensions/comment-components.ts:41-63

MEDIUM Passing the wrong tab content shape

Wrong:

import { parseMarkdown } from '@tanstack/markdown/parser'
import { docsMarkdownExtensions } from '@tanstack/markdown/extensions/docs'

const source = `<!-- ::start:tabs variant="files" -->

This variant does not turn prose into a file.

<!-- ::end:tabs -->`

export const document = parseMarkdown(source, {
  extensions: docsMarkdownExtensions(),
})

Correct:

import { parseMarkdown } from '@tanstack/markdown/parser'
import { docsMarkdownExtensions } from '@tanstack/markdown/extensions/docs'

const source = `<!-- ::start:tabs variant="files" -->

\`\`\`ts file="app.ts"
export const app = true
\`\`\`

<!-- ::end:tabs -->`

export const document = parseMarkdown(source, {
  extensions: docsMarkdownExtensions(),
})

The file transform silently returns the original component when it finds no direct code-block children; the other variants have similarly specific input contracts.

Source: src/extensions/tabs.ts:16-18

MEDIUM Expecting fence metadata to highlight code

Wrong:

import { renderHtml } from '@tanstack/markdown/html'

const source = `\`\`\`ts file="app.ts" {2}
const one = 1
const two = 2
\`\`\``

export const html = renderHtml(source)

Correct:

import { createHighlighter } from '@tanstack/highlight/core'
import { plaintext } from '@tanstack/highlight/languages/plaintext'
import { ts } from '@tanstack/highlight/languages/ts'
import { createTanStackMarkdownHighlighter } from '@tanstack/highlight/markdown'
import { renderHtml } from '@tanstack/markdown/html'

const source = `\`\`\`ts file="app.ts" {2}
const one = 1
const two = 2
\`\`\``

const highlighter = createHighlighter({
  languages: [plaintext, ts],
})

export const html = renderHtml(source, {
  highlighter: createTanStackMarkdownHighlighter(highlighter),
})

Fence metadata populates the AST and highlighter options, but tokenization, themes, and CSS stay outside this package.

Source: docs/core-concepts/syntax-profile.md

Boundaries

  • Docs transforms enrich trusted repository-authored content but do not execute MDX, JSX, or JavaScript expressions.
  • Highlighter output is trusted HTML. Keep highlighting at build time or on the server and apply the checks in production-pipelines.
  • Import individual extension entry points when the complete preset adds unused behavior or bundle cost.
  • Use custom-extensions when built-in comment components or transforms do not express the required deterministic syntax.
  • Use the React or Octane renderer skill to map emitted tags such as md-tab-panel and md-framework-panel to framework components.

References

Version History

  • 6936a01 Current 2026-09-21 23:55

Same Skill Collection

skills/custom-extensions/SKILL.md
skills/octane-rendering/SKILL.md
skills/production-pipelines/SKILL.md
skills/react-rendering/SKILL.md
skills/render-markdown/SKILL.md

Metadata

Files
0
Version
6936a01
Hash
3e3c22af
Indexed
2026-09-21 23:55

Accueil - Wiki
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-09-22 00:42
浙ICP备14020137号-1