TanStack
TanStack

Markdown

alpha

Markdown with an exit strategy.

Parse documents or accumulated AI output into a plain, serializable tree. Inspect it, cache it, index it, or render it as HTML, React, or Octane.

Docs
article.md3 blocks / live
---
title: Ship the docs
---

# One source

Parse it **once**. Render it where the product needs it.

- cache the tree
- index the text
- choose a renderer
root · 3 blocks
frontmatter · string
heading · depth: 1
paragraph · 3 inline nodes
list · 3 items

The durable layer

The AST is the product.

Parsing does not trap content inside a renderer. Edit the source and inspect the serializable tree, deterministic HTML, or React output.

Parsing locally
article.mdeditable source
One document, three views
{
  "type": "root",
  "frontmatter": "title: Durable content",
  "children": [
    {
      "type": "heading",
      "depth": 1,
      "id": "one-source-many-destinations",
      "children": [
        {
          "type": "text",
          "value": "One source, many destinations"
        }
      ]
    },
    {
      "type": "paragraph",
      "children": [
        {
          "type": "text",
          "value": "Parse the document "
        },
        {
          "type": "strong",
          "children": [
            {
              "type": "text",
              "value": "once"
            }
          ]
        },
        {
          "type": "text",
          "value": ", then keep the tree."
        }
      ]
    },
    {
      "type": "list",
      "ordered": false,
      "items": [
        {
          "type": "listItem",
          "children": [
            {
              "type": "paragraph",
              "children": [
                {
                  "type": "text",
                  "value": "cache it at the edge"
                }
              ]
            }
          ]
        },
        {
          "type": "listItem",
          "children": [
            {
              "type": "paragraph",
              "children": [
                {
                  "type": "text",
                  "value": "index its text"
                }
              ]
            }
          ]
        },
        {
          "type": "listItem",
          "children": [
            {
              "type": "paragraph",
              "children": [
                {
                  "type": "text",
                  "value": "render it with React or HTML"
                }
              ]
            }
          ]
        }
      ]
    },
    {
      "type": "blockquote",
      "children": [
        {
          "type": "paragraph",
          "children": [
            {
              "type": "text",
              "value": "The renderer can change. The document does not."
            }
          ]
        }
      ]
    }
  ]
}
21 typed nodes236 source charactersJSON serializable

Accumulated AI responses

Stream the text. Keep the parser stateless.

Append each chunk and pass the complete string through Markdown. The optional streaming profile reparses synchronously, with no incremental state to coordinate or recover.

Complete
Accumulated source
# Streaming response

The model can send **ordinary Markdown** as it thinks.

- completed blocks stay stable
- unfinished markers stay out of the way
- React and HTML stay in sync

```ts
const text = responseSoFar
return render(text)
```

[Unsafe links stay text-only](javascript:alert("nope"))
React output

Streaming response

The model can send ordinary Markdown as it thinks.

  • completed blocks stay stable
  • unfinished markers stay out of the way
  • React and HTML stay in sync
const text = responseSoFar
return render(text)

Unsafe links stay text-only

Complete input, reparsed294 / 294 chars

A deliberate profile

It does less Markdown on purpose.

Technical docs need a known vocabulary, not an open-ended compiler platform. New syntax has to justify its bytes, ambiguity, and maintenance cost.

Inside the profile
  • Headings and emphasis
  • Lists and task lists
  • Tables and footnotes
  • Fenced code with metadata
  • Links, images, and references
  • Frontmatter
Outside the contract
  • Every CommonMark edge case
  • Arbitrary async plugin chains
  • MDX or JSX evaluation
  • A bundled highlighter
  • A general HTML sanitizer

Boundary behavior

Unsafe surprises are opt-in.

Raw HTML starts escaped, executable URL schemes are stripped, and text, attributes, and code are encoded at render time.

Untrusted input
<script>alert("not today")</script>

[Run code](javascript:alert("nope"))

**Trusted Markdown still renders.**
Deterministic HTML
<p>&lt;script&gt;alert(&quot;not today&quot;)&lt;/script&gt;</p>
<p>Run code</p>
<p><strong>Trusted Markdown still renders.</strong></p>
HTML escapedExecutable URL removedMarkdown preserved

Size ledger

A parser should not outweigh the page.

Split entry points keep the parser, renderers, framework adapters, and docs extensions independent. Import only the layer the page needs.

TanStack parser4.9 KB
marked12.5 KB
unified stack36.8 KB
markdown-it52.7 KB
6.7 KB
HTML renderer
6.7 KB
React adapter
6.7 KB
Octane adapter
2.4 KB
docs preset

Content, then color

Syntax highlighting stays outside the parser.

Code fences carry language and metadata. An explicit highlighter renders them later, so the core never silently imports a grammar engine.

Companion, not dependency

TanStackHighlight

Synchronous highlighting for the code fences the document model already understands.

Explore Highlight

Partners

Gold
Cloudflare
Lovable
Netlify
Railway
CodeRabbit
Silver
WorkOS
SerpApi
Clerk
OpenRouter
AG Grid
Bronze
Unkey
Prisma
Electric
Sentry
OSS Sponsors

Sponsors get special perks like private discord channels, priority issue requests, and direct support!