marked-terminal
marked-terminal is a renderer extension that turns Markdown parsed by Marked into ANSI-styled terminal text. It formats headings, emphasis, code, blockquotes, lists, task boxes, links, images, and GitHub-flavored tables, with optional syntax highlighting, emoji replacement, text reflow, and custom Chalk-style functions. It is a renderer, not a command-line program, pager, Markdown parser, sanitizer, or interactive UI framework.
Useful when Marked is already part of the CLI and polished tables, highlighting, and styles justify its dependency stack. For one paragraph of help text, plain strings or a smaller direct converter are easier to type, test, and keep compatible.
Use it if
- Your Node CLI already uses Marked and needs readable colored help, release notes, README excerpts, or generated reports
- You need GitHub-flavored tables and fenced-code highlighting in plain terminal output
- You want to customize styles, wrapping width, bullets, table borders, links, emoji, or image rendering
- You need both ESM and CommonJS entry points on Node 16 or newer
- You do not already need Marked: it is a required peer dependency, while a self-contained Markdown-to-ANSI package may be less setup
- You need TypeScript declarations: version 7.3.0 publishes no `types` field or declaration file, so strict TypeScript projects must add a local module declaration
- You track the newest Marked automatically: the peer range is `>=1 <16`, so Marked 16 or later is outside the package's declared compatibility
- You need a tiny dependency tree: the package brings ansi-escapes, ansi-regex, Chalk, cli-highlight, cli-table3, node-emoji, and supports-hyperlinks, and the published package is about 1.96 MB unpacked
- You render untrusted Markdown directly to terminals: the renderer styles content but is not an ANSI control-sequence sanitizer, so hostile input needs filtering before print
Setup reality
Install both sides of the integration with `npm install marked marked-terminal`; Marked is a peer, not bundled. Version 7.3.0 requires Node 16 or newer and declares compatibility with Marked versions from 1 through 15, excluding 16. The current API is `marked.use(markedTerminal(options, highlightOptions))`. The README's first example shows that extension form, but its later API and customization sections still describe the older `new TerminalRenderer()` and `marked.setOptions()` style. Do not copy that older section into new version 7 code. ESM users import the named `markedTerminal` export, while CommonJS users can destructure the same name from `require`; a default Renderer export remains mainly for compatibility. There are no TypeScript declarations in the package, which means a TypeScript CLI needs a small local declaration or relaxed typing. Color behavior depends on Chalk and the output environment, and hyperlinks depend on terminal support. `width` only affects output when `reflowText` is true; use `process.stdout.columns` with a fallback instead of assuming an interactive TTY. Syntax highlighting is delegated to cli-highlight and silently falls back to the code style when the language is unsupported. Custom style functions often import Chalk, so declare Chalk as your own direct dependency rather than relying on marked-terminal's internal installation. Tables add cli-table3 behavior and can become unreadable in narrow logs. Raw Markdown content is not made safe for a terminal, pager, or CI annotation channel; strip control characters from untrusted text before parsing. Also register the extension once at process startup, since repeatedly mutating Marked's global configuration can make tests and commands influence each other.
Patterns
Render Markdown with the current extension APIrender-markdown
import {marked} from 'marked';
import {markedTerminal} from 'marked-terminal';
marked.use(markedTerminal());
const output = marked.parse('# Status\n\n**Ready** to deploy.');
process.stdout.write(output);Register the extension once. Version 7's current path is marked.use(markedTerminal()), not the older constructor shown later in the README.
Render from a CommonJS CLIuse-commonjs
const {marked} = require('marked');
const {markedTerminal} = require('marked-terminal');
marked.use(markedTerminal());
console.log(marked.parse('## Results'));The package exports a require condition on Node, but the named extension export is clearer than relying on the compatibility default Renderer.
Reflow prose to terminal widthwrap-to-terminal
const width = Math.max(40, process.stdout.columns || 80);
marked.use(markedTerminal({
reflowText: true,
width,
}));width is ignored unless reflowText is true. CI output often has no stdout.columns, so keep a fallback.
Customize heading and code stylescustomize-colors
import chalk from 'chalk';
marked.use(markedTerminal({
firstHeading: chalk.cyan.bold,
heading: chalk.blue.bold,
codespan: chalk.yellow,
blockquote: chalk.gray.italic,
}));Add Chalk as a direct dependency when importing it. Do not rely on a transitive package being resolvable from your application.
Configure fenced-code highlightinghighlight-code
marked.use(markedTerminal(
{code: (text) => text},
{ignoreIllegals: true},
));
console.log(marked.parse('```js\nconst answer = 42;\n```'));Highlight options pass to cli-highlight. Unknown or failing languages fall back to the configured code style instead of failing the render.
Tune table borders and paddingrender-tables
marked.use(markedTerminal({
tableOptions: {
chars: {'top': '-', 'top-mid': '+', 'top-left': '+', 'top-right': '+'},
style: {'padding-left': 1, 'padding-right': 1},
},
}));tableOptions go directly to cli-table3. Test narrow terminals because wide cells can dominate log output.
Keep colon codes as plain textdisable-emoji
marked.use(markedTerminal({emoji: false}));
console.log(marked.parse('Build status: :warning:'));Emoji replacement is enabled by default and uses node-emoji, so disable it when colon-delimited text must remain exact.
Remove Markdown hash prefixes from headingshide-heading-prefix
marked.use(markedTerminal({
showSectionPrefix: false,
}));The default includes hash characters before terminal headings, which is useful for hierarchy but noisy in compact help output.
Replace image syntax with accessible textcustomize-images
marked.use(markedTerminal({
image: (href, title, text) =>
`[image: ${text || title || 'untitled'}] ${href}\n`,
}));Terminals cannot render Markdown images. A custom function can preserve alt text and the URL for logs and screen readers.
Preserve HTML entitiesdisable-entity-unescape
marked.use(markedTerminal({unescape: false}));
console.log(marked.parse('Show `<tag>` literally.'));Entity unescaping is enabled by default. Disable it when exact encoded text matters more than display readability.
Strip control characters from untrusted Markdownsanitize-terminal-input
const safeMarkdown = untrustedMarkdown
.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g, '');
process.stdout.write(marked.parse(safeMarkdown));marked-terminal is a formatter, not a terminal escape sanitizer. Apply a security policy appropriate to the terminal or log sink before rendering hostile text.
Add a minimal local TypeScript declarationdeclare-types
// marked-terminal.d.ts
declare module 'marked-terminal' {
import type {MarkedExtension} from 'marked';
export function markedTerminal(
options?: Record<string, unknown>,
highlightOptions?: Record<string, unknown>,
): MarkedExtension;
}Version 7.3.0 ships no declarations. Expand this local type only for options your project actually uses and verify it on upgrades.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| markdown-to-ansi | npm | You want a direct Markdown-to-ANSI conversion package without wiring a Marked renderer yourself |
| marked-man | npm | Your output target is Unix man-page roff rather than colored interactive terminal text |
| marked | npm | You only need tokenization or HTML and are willing to write a very small renderer for your limited CLI syntax |