Agent SkillsTanStack/markdown › react-rendering

react-rendering

GitHub

基于 @tanstack/markdown/react 在 React 中渲染 Markdown,支持组件替换、AST 复用及服务端静态渲染。

skills/react-rendering/SKILL.md TanStack/markdown

Trigger Scenarios

需要在 React 应用中渲染 Markdown 内容 自定义 Markdown 元素到 React 组件的映射 进行 React 服务端静态 HTML 生成

Install

npx skills add TanStack/markdown --skill react-rendering -g -y
More Options

Use without installing

npx skills use TanStack/markdown@react-rendering

指定 Agent (Claude Code)

npx skills add TanStack/markdown --skill react-rendering -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": "react-rendering",
    "sources": [
        "TanStack\/markdown:docs\/guides\/react.md",
        "TanStack\/markdown:docs\/reference\/react.md",
        "TanStack\/markdown:docs\/guides\/extensions.md",
        "TanStack\/markdown:docs\/core-concepts\/security.md",
        "TanStack\/markdown:src\/react.ts",
        "TanStack\/markdown:tests\/ssr-react.test.tsx"
    ],
    "metadata": {
        "type": "framework",
        "library": "@tanstack\/markdown",
        "framework": "react",
        "library_version": "0.0.15"
    },
    "requires": [
        "render-markdown"
    ],
    "description": "Render Markdown source or a MarkdownDocument with @tanstack\/markdown\/react using Markdown, renderMarkdownReact, component replacements, and React static SSR. Load for React article components, emitted-tag mappings, pre-parsed documents, custom elements, or renderer parity.\n"
}

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

React Rendering

Setup

Render source directly and keep the surrounding semantic element in the application:

import { Markdown } from '@tanstack/markdown/react'

export function Article({ source }: { source: string }) {
  return (
    <article>
      <Markdown>{source}</Markdown>
    </article>
  )
}

Markdown returns a React fragment. The React adapter requires React 18 or newer.

Hooks and Components

Replace emitted links with an application component

import {
  Markdown,
  type MarkdownComponents,
} from '@tanstack/markdown/react'

const components = {
  a(props) {
    const external = props.href?.startsWith('http') ?? false
    return (
      <a
        {...props}
        rel={external ? 'nofollow noopener noreferrer' : props.rel}
        target={external ? '_blank' : props.target}
      />
    )
  },
} satisfies MarkdownComponents

export function Article({ source }: { source: string }) {
  return (
    <article>
      <Markdown components={components}>{source}</Markdown>
    </article>
  )
}

Known intrinsic keys infer their matching React props. Arbitrary extension component tag names remain supported.

Parse once before repeated React rendering

import { parseMarkdown } from '@tanstack/markdown/parser'
import { Markdown } from '@tanstack/markdown/react'

const document = parseMarkdown('# Cached article\n\nRendered from an AST.')

export function Article() {
  return (
    <article>
      <Markdown>{document}</Markdown>
    </article>
  )
}

Apply parser options and document transforms when creating the document; renderer options cannot retroactively change its structure.

Render static React markup on the server

import { renderToStaticMarkup } from 'react-dom/server'
import { Markdown } from '@tanstack/markdown/react'

const source = '# Server-rendered article'

export const html = renderToStaticMarkup(
  <article>
    <Markdown>{source}</Markdown>
  </article>,
)

Core static output is tested for structural equivalence with renderHtml().

Compose through the lower-level node API

import type { ReactNode } from 'react'
import { parseMarkdown } from '@tanstack/markdown/parser'
import { renderMarkdownReact } from '@tanstack/markdown/react'

const document = parseMarkdown('# Parsed once')

export function ArticleBody(): ReactNode {
  return renderMarkdownReact(document)
}

Use renderBlockReact or renderInlineReact only when custom tree composition needs individual public AST nodes.

Common Mistakes

HIGH Injecting HTML instead of React nodes

Wrong:

import { renderHtml } from '@tanstack/markdown'

export function Article({ source }: { source: string }) {
  const html = renderHtml(source)
  return <article dangerouslySetInnerHTML={{ __html: html }} />
}

Correct:

import { Markdown } from '@tanstack/markdown/react'

export function Article({ source }: { source: string }) {
  return (
    <article>
      <Markdown>{source}</Markdown>
    </article>
  )
}

The HTML-string path bypasses React component replacement and creates an unnecessary trusted-HTML boundary.

Source: docs/guides/react.md

HIGH Mapping AST node names instead of tags

Wrong:

import type { ComponentProps } from 'react'
import { Markdown } from '@tanstack/markdown/react'

function ArticleLink(props: ComponentProps<'a'>) {
  return <a {...props} data-navigation="article" />
}

export function Article() {
  return (
    <Markdown components={{ link: ArticleLink }}>
      {'Read the [guide](/guide).'}
    </Markdown>
  )
}

Correct:

import type { ComponentProps } from 'react'
import { Markdown } from '@tanstack/markdown/react'

function ArticleLink(props: ComponentProps<'a'>) {
  return <a {...props} data-navigation="article" />
}

export function Article() {
  return (
    <Markdown components={{ a: ArticleLink }}>
      {'Read the [guide](/guide).'}
    </Markdown>
  )
}

The map is keyed by emitted tag names such as a, not AST discriminants such as link.

Source: docs/guides/react.md

HIGH Expecting HTML extension hooks in React

Wrong:

import type { MarkdownExtension } from '@tanstack/markdown'
import { Markdown } from '@tanstack/markdown/react'

const paragraphHtml: MarkdownExtension = {
  name: 'paragraph-html',
  renderHtml(node) {
    return node.type === 'paragraph'
      ? '<aside>Rendered only by the HTML renderer</aside>'
      : undefined
  },
}

export function Article() {
  return <Markdown extensions={[paragraphHtml]}>Ordinary content</Markdown>
}

Correct:

import type { MarkdownExtension } from '@tanstack/markdown'
import type { PropsWithChildren } from 'react'
import { Markdown } from '@tanstack/markdown/react'

const panelExtension: MarkdownExtension = {
  name: 'panel',
  transformDocument(document) {
    return {
      ...document,
      children: [
        {
          type: 'component',
          name: 'panel',
          tagName: 'doc-panel',
          attributes: {},
          children: document.children,
        },
      ],
    }
  },
}

function Panel({ children }: PropsWithChildren) {
  return <aside className="documentation-panel">{children}</aside>
}

export function Article() {
  return (
    <Markdown
      components={{ 'doc-panel': Panel }}
      extensions={[panelExtension]}
    >
      Ordinary content
    </Markdown>
  )
}

MarkdownExtension.renderHtml is HTML-specific; portable custom output uses ComponentNode and an emitted-tag component mapping.

Source: docs/guides/extensions.md

CRITICAL Enabling raw HTML for untrusted input

Wrong:

import { Markdown } from '@tanstack/markdown/react'

export function UserPost({ source }: { source: string }) {
  return <Markdown allowHtml>{source}</Markdown>
}

Correct:

import { Markdown } from '@tanstack/markdown/react'

export function UserPost({ source }: { source: string }) {
  return <Markdown>{source}</Markdown>
}

With allowHtml, React uses dangerouslySetInnerHTML; TanStack Markdown does not sanitize that raw HTML.

Source: docs/core-concepts/security.md

HIGH Tension: renderer parity versus React customization

Core React SSR is tested against renderHtml(). React component replacements, raw HTML, highlighter output, and HTML-only extension hooks are application-controlled boundaries outside that parity guarantee.

See also: custom-extensions for portable ComponentNode output.

Cross-References

  • render-markdown - shared MarkdownInput, parser options, AST behavior, and syntax profile.
  • custom-extensions - emit custom tags that React can replace through components.
  • production-pipelines - audit raw HTML, highlighter output, untrusted content, and SSR boundaries.

Version History

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

Same Skill Collection

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

Metadata

Files
0
Version
6936a01
Hash
75895092
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