Markdown
Render markdown content with syntax-aware styling and optional Tree-sitter highlighting for code blocks.
Basic usage
Renderable API
import { MarkdownRenderable, SyntaxStyle, RGBA, createCliRenderer } from "@opentui/core"
const renderer = await createCliRenderer()
const syntaxStyle = SyntaxStyle.fromStyles({
"markup.heading.1": { fg: RGBA.fromHex("#58A6FF"), bold: true },
"markup.list": { fg: RGBA.fromHex("#FF7B72") },
"markup.raw": { fg: RGBA.fromHex("#A5D6FF") },
default: { fg: RGBA.fromHex("#E6EDF3") },
})
const markdown = new MarkdownRenderable(renderer, {
id: "readme",
width: 60,
content: "# Hello\n\n- One\n- Two\n\n```ts\nconst x = 1\n```",
syntaxStyle,
})
renderer.root.add(markdown)
Concealment
Hide markdown markers (backticks, emphasis markers, etc.) when conceal is true:
const markdown = new MarkdownRenderable(renderer, {
content: "**bold** and `code`",
syntaxStyle,
conceal: true,
})
Use concealCode to control concealment inside fenced code blocks independently (false by default).
Streaming updates
Enable streaming mode for incremental updates. Keep it true while appending chunks, then set markdown.streaming = false when complete to finalize trailing block parsing. Tables include trailing partial rows, with missing cells rendered empty.
const markdown = new MarkdownRenderable(renderer, {
content: "",
syntaxStyle,
streaming: true,
})
markdown.content += "# Live log\n"
markdown.content += "- line 1\n"
markdown.streaming = false
Markdown tables
Markdown tables support tableOptions for sizing, wrapping, borders, and selection.
const markdown = new MarkdownRenderable(renderer, {
content: "| Service | Status |\n| --- | --- |\n| api | ok |",
syntaxStyle,
tableOptions: {
widthMode: "full",
columnFitter: "balanced",
wrapMode: "word",
cellPadding: 1,
borders: true,
outerBorder: true,
borderStyle: "rounded",
borderColor: "#6b7280",
selectable: true,
},
})
tableOptions
| Option | Type | Default | Description |
|---|---|---|---|
widthMode | "content" | "full" | "full" | "full" expands columns to fill available width |
columnFitter | "proportional" | "balanced" | "proportional" | How columns shrink when space is constrained |
wrapMode | "none" | "char" | "word" | "word" | Wrapping mode inside each table cell |
cellPadding | number | 0 | Padding on all sides of each cell |
borders | boolean | true | Enable inner and outer borders |
outerBorder | boolean | borders | Override outer border visibility |
borderStyle | BorderStyle | "single" | Table border character set |
borderColor | ColorInput | conceal fg or #888888 | Border color for markdown tables |
selectable | boolean | true | Enable table cell text selection |
Custom node rendering
Override rendering for a token and fall back to default rendering:
const markdown = new MarkdownRenderable(renderer, {
content: "# Title\n\nHello",
syntaxStyle,
renderNode: (token, context) => {
if (token.type === "heading") {
return context.defaultRender()
}
return undefined
},
})
Construct API
Not available yet. Use
MarkdownRenderablefor now.
Properties
| Property | Type | Default | Description |
|---|---|---|---|
content | string | "" | Markdown source |
syntaxStyle | SyntaxStyle | required | Style definitions for tokens |
conceal | boolean | true | Hide markdown markers in markdown text |
concealCode | boolean | false | Hide markers inside fenced code blocks |
streaming | boolean | false | Incremental mode; set false to finalize |
tableOptions | MarkdownTableOptions | - | Options for markdown table rendering |
treeSitterClient | TreeSitterClient | - | Custom Tree-sitter client for code blocks |
renderNode | (token: Token, context: RenderNodeContext) => Renderable | null | undefined | - | Custom render hook per markdown block |