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

OptionTypeDefaultDescription
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
cellPaddingnumber0Padding on all sides of each cell
bordersbooleantrueEnable inner and outer borders
outerBorderbooleanbordersOverride outer border visibility
borderStyleBorderStyle"single"Table border character set
borderColorColorInputconceal fg or #888888Border color for markdown tables
selectablebooleantrueEnable 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 MarkdownRenderable for now.

Properties

PropertyTypeDefaultDescription
contentstring""Markdown source
syntaxStyleSyntaxStylerequiredStyle definitions for tokens
concealbooleantrueHide markdown markers in markdown text
concealCodebooleanfalseHide markers inside fenced code blocks
streamingbooleanfalseIncremental mode; set false to finalize
tableOptionsMarkdownTableOptions-Options for markdown table rendering
treeSitterClientTreeSitterClient-Custom Tree-sitter client for code blocks
renderNode(token: Token, context: RenderNodeContext) => Renderable | null | undefined-Custom render hook per markdown block