- Surface
- Core
- Entry point
parse,toRenderPlan- Responsibility
- AST, diagnostics, safe links, positions, and render parts.
Docs Platform
Markdown
Markdown is a data pipeline, not a string-to-HTML shortcut. The core parser produces a stable AST and diagnostics; the render plan turns that AST into framework-neutral parts; each adapter decides how those parts become DOM.
Live pipeline
The shipped parser and render plan, re-run on every edit. The output uses the same styled components the rest of the kit ships — Blockquote, List, task items, code blocks.
Source · edit me
Rendered · the shipped pipeline
Release 0.9
Grassroot's markdown is a data pipeline: parse to an AST, plan the render, then map plan nodes to components.
Blockquotes, task lists, and tables all resolve to the same styled components the rest of the kit ships.
const plan = renderPlan(parse(source));Explore the layers
The headless contract lives in
@grassroot/ui-headless-core/markdown and is re-exported by
@grassroot/ui-headless-{react,vue,solid,svelte,angular}/markdown.
The styled roots live in the corresponding
@grassroot/ui-* packages. Markdown is intentionally
stateless: it has no machine registry item and does not pull in
@grassroot/ui-machines.
parse, toRenderPlanMarkdownMarkdownmountMarkdown- Surface
- Headless
- Entry point
Markdown- Responsibility
- SSR-safe framework markup with no visual recipe.
- Surface
- Styled
- Entry point
Markdown- Responsibility
- Maps headings, code, callouts, tasks, details, lists, figures, and footnotes to Grassroot components.
- Surface
- Vanilla
- Entry point
mountMarkdown- Responsibility
- Framework-free DOM mount using text nodes for untrusted content.
Parse to render plan
parse(source, options) handles headings, paragraphs, fenced
and indented code, lists and tasks, blockquotes and callouts, tables,
details, reference links, footnotes, inline emphasis, code spans,
autolinks, images, and hard breaks. Every block has a deterministic id;
source positions and diagnostics are optional outputs controlled by the
parse options.
const document = parse(source, {
gfm: true,
footnotes: true,
callouts: true,
sourcePositions: true,
});
const plan = toRenderPlan(document, {
code: { ts: (value, language) => highlight(value, language) },
});
Renderer overrides are keyed by semantic node type, while code highlighters are keyed by language and win over the default tokenizer. Render tokens are grouped per source line so adapters can preserve whitespace and line structure during SSR.
Security
Raw HTML is represented as html-blocked and rendered as
escaped text. Link and image destinations pass through
sanitizeUrl; unsafe schemes such as
javascript:, vbscript:, and unsafe data URLs are
dropped with a diagnostic. The vanilla and framework adapters use text
nodes or framework interpolation, never an HTML sink for Markdown
source.
Streaming and editing
parseIncremental returns the next document plus an
incremental state and preserves ids for unchanged blocks. The editor
bridge converts the AST to a small ProseMirror/Tiptap-shaped node model
and serializes edited nodes back through the same parser. Canonical
serialization normalizes headings, lists, fences, and spacing so a
parse/serialize cycle is deterministic.
Editor round-trip
The editor bridge is deliberately structural. It exposes plain JSON
nodes and marks so the Rich Text Editor tranche can consume Markdown
without a package dependency on the editor machine. Formatting is
canonical rather than source-preserving: edited nodes serialize through
serializeMarkdown, then return to the AST through
parse.
const nodes = parseToEditorNodes(source);
const edited = editorNodesToAst(nodes);
const markdown = serializeMarkdown(edited);
const document = parse(markdown);
M-4's EditorBridgeNode contract is frozen as of this
close-out; the Editor plan's E-4 slice should re-review it against its
actual model/schema.ts before consuming it, per the plan's
own risk table.
Framework adapters
React, Vue, Solid, Svelte, and Angular expose the same source/AST/options contract. Their headless roots are SSR-safe and avoid effects for static rendering; the styled roots add the framework's existing Heading, CodeBlock, Alert, Checkbox, and Collapsible primitives where available. The browser contract exercises the same scenarios across all five adapters and the vanilla mount.