Markdown
Headless Markdown renderer. Bring your own parser (remark / marked) — Wire UI exposes the render parts via the components map so you control every element. Pass a pre-parsed nodes tree, or content plus a parse function.
Features
mdast-compatible node tree.nodes, or content + parse.components map.Example
Wire UI
A headless library with zero CSS and full control.
- Compound components
- data-* styling
- Install
@wire-ui/react
Style it however you like.
npm i @wire-ui/reactAnatomy
import { Markdown } from '@wire-ui/react'
import type { MarkdownNode } from '@wire-ui/react'
const nodes: MarkdownNode[] = [
{ type: 'heading', depth: 2, children: [{ type: 'text', value: 'Wire UI' }] },
{ type: 'paragraph', children: [{ type: 'text', value: 'A headless library.' }] },
]
<Markdown nodes={nodes} />Styling
Markdown ships unstyled, semantic HTML. The cleanest way to style it is to override renderers through the components map — each entry receives { node, children } and returns your own element. Overrides merge over the built-ins, so you only need to supply the node types you care about.
The default renderers set a few data attributes you can also target with CSS: data-inline on inline code, data-language on fenced code, and data-task / data-checked on task-list items.
/* Default fenced-code block */
pre[data-language] code { font-family: ui-monospace, monospace; }
/* Inline code */
code[data-inline] { background: #f5f5f5; padding: 0 0.25rem; }
/* Task-list items */
li[data-task] { list-style: none; }
li[data-checked] { text-decoration: line-through; }Wiring a real parser
parse turns a raw string into the normalized node tree. Wrap remark (whose mdast output matches the MarkdownNode shape) or map another parser’s output onto it.
import { Markdown } from '@wire-ui/react'
import { unified } from 'unified'
import remarkParse from 'remark-parse'
const parse = (content: string) =>
unified().use(remarkParse).parse(content).children
<Markdown content="# Hello\n\nWorld" parse={parse} />When content is provided without parse, the raw string is rendered as a single paragraph as a graceful fallback.
Markdown props
| Prop | Type | Default | Description |
|---|---|---|---|
nodes | MarkdownNode[] | — | Pre-parsed node tree. Provide this, or content + parse |
content | string | — | Raw Markdown source. Parsed with parse when provided |
parse | (content: string) => MarkdownNode[] | — | Turns content into normalized nodes — wrap remark / marked here |
components | MarkdownComponents | — | Override the renderer used for one or more node types |
Markdown also accepts all standard <div> HTML attributes (except children).
MarkdownNode
| Field | Type | Description |
|---|---|---|
type | MarkdownNodeType | Node type (heading, paragraph, text, link, …). Unknown strings fall back to rendering children |
value | string | Literal text for leaf nodes (text, inlineCode, code) |
depth | number | Heading level, 1–6 |
url | string | Destination for link / image |
title | string | null | Advisory title for link / image |
alt | string | Alternate text for image |
ordered | boolean | Whether a list is ordered |
start | number | Starting number for an ordered list |
checked | boolean | null | Task-list state for a listItem (null when not a checkbox item) |
lang | string | Language hint for a fenced code block |
children | MarkdownNode[] | Child nodes |
Component props (renderer)
Each entry in components is a component receiving these props:
| Prop | Type | Description |
|---|---|---|
node | MarkdownNode | The node being rendered |
children | ReactNode | Pre-rendered child nodes (or the leaf value) |
Data attributes
| Attribute | Element | Values |
|---|---|---|
data-inline | Default inlineCode | Always present |
data-language | Default code <pre> / <code> | The lang value (when present) |
data-task | Default task listItem | Present for checkbox items |
data-checked | Default task listItem | Present when the task is checked |
These come from the built-in renderers; supply your own via components to change them.