---
title: "Render Markdown in the Terminal for Node CLIs and Coding Agents"
description: "Print Markdown as styled ANSI output in Node.js CLIs and coding agents, with highlighted code, tables, and alerts, using @comark/ansi."
canonical_url: "https://comark.dev/use-cases/cli-and-agents"
---
# Render Markdown in the Terminal for Node CLIs and Coding Agents

> Print Markdown as styled ANSI output in Node.js CLIs and coding agents, with highlighted code, tables, and alerts, using @comark/ansi.

Use `@comark/ansi` to turn Markdown into a string with ANSI styles. `printAnsi()` writes it to the terminal, and `renderAnsi()` returns it so you can write it yourself.

Coding agents and CLIs often print Markdown: release notes, help text, or model answers. Raw Markdown in a terminal shows `**` and `#` characters. `@comark/ansi` styles headings, emphasis, lists, links, tables, GitHub alerts, and code blocks.

## Print a Markdown file

Install the package, and `shiki` for code highlighting:

```bash [Terminal]
npm install @comark/ansi shiki
```

This script reads a file and prints it with highlighted code and math:

```typescript [render.ts]
import { readFile } from 'node:fs/promises'
import { printAnsi } from '@comark/ansi'
import shiki from '@comark/ansi/plugins/shiki'
import math, { Math } from '@comark/ansi/plugins/math'

const md = await readFile('source.md', 'utf-8')

await printAnsi(md, {
  plugins: [shiki(), math()],
  components: { Math },
})
```

Run it with `node render.ts`. The [ANSI example](https://github.com/comarkdown/comark/tree/main/examples/3.cli/ansi) contains a sample file with every supported element.

## Control width, colors, and output

`printAnsi()` writes to `stdout` by default. These options change the output:

- **`width`** sets the width of horizontal rules and code block headers. The default is `80`.
- **`colors`** turns ANSI escape codes on or off. It is `false` when the `NO_COLOR` environment variable is set.
- **`writer`** receives the output string, for example to write to `stderr`.

```typescript [cli.ts]
import { createAnsiPrinter } from '@comark/ansi'
import shiki from '@comark/ansi/plugins/shiki'

const print = createAnsiPrinter({
  plugins: [shiki()],
  width: process.stdout.columns ?? 80,
  writer: output => process.stderr.write(output),
})

await print('> [!WARNING]\n> This command deletes the build cache.')
await print('| Step | Status |\n| --- | --- |\n| Build | Done |')
```

`createAnsiPrinter()` and `createAnsiRenderer()` set up the parser once. Use them when you print more than one document.

## Render custom components

Map a component tag to a function that returns a string. The function receives the node and a `render()` helper for its children:

```typescript [cli.ts]
import { renderAnsi } from '@comark/ansi'

const output = await renderAnsi('::badge{type="success"}\nBuild passed\n::', {
  components: {
    badge: async ([, attrs, ...children], { render }) =>
      `[${String(attrs.type).toUpperCase()}] ${await render(children)}`,
  },
})
// [SUCCESS] Build passed
```

## Show streaming model output

`@comark/ansi` renders complete strings, and it has no `streaming` option. Because `autoClose` closes unfinished syntax, you can render the partial text at any time.

The following code is a pattern, not a Comark API. It re-renders the accumulated text after each chunk, and redraws the screen:

```typescript [agent.ts]
import { streamText } from 'ai'
import { createAnsiRenderer } from '@comark/ansi'

const render = createAnsiRenderer({ width: process.stdout.columns ?? 80 })
const result = streamText({ model: 'anthropic/claude-sonnet-4.6', prompt: 'Explain git rebase.' })

let text = ''
for await (const chunk of result.textStream) {
  text += chunk
  // Clear the screen, move the cursor to the top, then print the new frame
  process.stdout.write('\x1b[2J\x1b[H' + await render(text))
}
```

A full redraw replaces the scrollback on each frame. For long answers, print the final output once when the stream ends.

## FAQ

::accordion
  :::accordion-item{label="How do I turn off colors in CI?"}
  Set the `NO_COLOR` environment variable, or pass `colors: false`. The output then contains no escape codes.
  :::

  :::accordion-item{label="Can I render a document that I already parsed?"}
  Yes. Pass it to `renderAnsiFromDocument()`. It renders without a parse step.
  :::

  :::accordion-item{label="Can I change how a heading or link looks?"}
  Yes. Pass a function for the native tag, for example `h1` or `a`, in `components`. See [overriding terminal output](https://comark.dev/rendering/ansi#overriding-terminal-output).
  :::
::

## Next steps

- [ANSI rendering reference](https://comark.dev/rendering/ansi)
- [Shiki plugin](https://comark.dev/plugins/built-in/shiki)
- [Render streaming Markdown from an LLM](https://comark.dev/use-cases/ai-chat-streaming)
- [Markdown to HTML for emails and RSS](https://comark.dev/use-cases/email-and-rss)

---

- [ANSI rendering](https://comark.dev/rendering/ansi)
- [Shiki plugin](https://comark.dev/plugins/built-in/shiki)


## Sitemap

See the full [sitemap](https://comark.dev/sitemap.md) for all pages.
