termimad: Markdown rendering for a Rust terminal application
A library to display rich (Markdown) snippets and texts in a rust terminal application
At a glance
- What is it?
- termimad is a Rust library for showing Markdown-derived text in a terminal, with a skinning system, dynamic width discovery, table alignment, scrolling and templates that keep user-supplied values out of the parsed Markdown. The README is unusually explicit about what it is not, which is a large part of its appeal.
- Who is it for?
- Adopt termimad when you are building a terminal application that needs to show Markdown-like help pages, tables and snippets with a configurable look, and when wrapping and width handling are the parts you would otherwise get wrong. Do not adopt it as a TUI framework, as a complete Markdown implementation, or for a tool that needs syntax colouring, ordered lists or links.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 25 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Structure, data and skin separated in a terminal
Termimad is described as a set of cross-platform utilities for CLI and TUI applications, and the design idea is that Markdown is used as the intermediate format so the structure of a text, its data and its skin can be kept apart. The skin is the part that decides colours and attributes, the text is Markdown, and the rendering is the third thing.
That separation is what makes the library more than a pretty printer. A program can offer one help screen and one error screen with different skins without duplicating the text, and a text can be reused in a scrollable view and in a one-line inline snippet without changing.
The stack underneath is stated in the introduction: crossterm is the backend for styling and events, crokey provides key combination support, and crossbeam is used for event passing. The library also states a focus on total control and performance, with no active loop, and a striving to be correct even on Unicode and wide characters. That last point is the one that bites in practice, since a table that miscounts wide characters is broken in a way that only shows up in some terminals.
The project states plainly what it is not
The README has a short section headed by what termimad is not, and it is worth reading before anything else. First, it is not a TUI framework, and the advice given is that unless you have serious performance concerns or want precise control, you will find it much easier and faster to build a TUI application with one of the TUI frameworks of the Rust ecosystem.
Second, it is not a generic Markdown renderer. The stated goal is not to display any Markdown text with its various extensions, on the grounds that a terminal is not really fit for that, but to improve the display of texts in a terminal application when both the text and the skin need to be easily configured.
Third, and this is the most useful line in the README, termimad is not consistent or complete in any way. A library that says this about itself is saving you the disappointment of discovering it later. What you get is a set of utilities with a coherent purpose, not a specification-compliant renderer with a test suite proving every corner.
The practical reading is that termimad is a rendering component you call from a program you have otherwise designed yourself. It draws and it aligns. It does not own your event loop, your screen layout or your input handling.
The feature matrix is honest about gaps
The README includes a Markdown table of its own capabilities, rendered by the library, and it is more informative than most feature lists because it names the absences. Tables are supported, pipe based, with or without alignments. Italic and bold work and are star based. Inline code works with backquotes, and the table says it works inside tables too. Code blocks are supported with tabs or code fences. Horizontal rules work with three or more dashes. Quotes work, and lists are supported for unordered items.
Three entries are not. Syntax colouring is marked as not supported, which is the most consequential gap for a developer tool, since a code block is exactly where colour would help most. Ordered lists are marked as not supported, a surprising hole in a Markdown subset aimed at help pages. Links are marked as not supported, with the aside that the terminal already handles raw URLs, which is a fair argument but does not help if you want a link label.
Crossed text is the entry with a joke in it, marked as not yet and then crossed out with the note that it now works. The list is a snapshot of the library's real scope, and the second crossing shows the matrix is maintained by hand alongside the code.
Width discovery, wrapping and scrolling
Wrapping, table balancing and scrolling are named as the essential features, and the reason is a problem specific to the terminal. A text or a table can be displayed in a part of the screen whose size is not known in advance, scrollable if desired, with a width discovered dynamically.
The README shows the same Markdown table rendered at three different widths, with different results each time, which is the point: a table is not laid out once and printed, it is laid out against the width available. Columns are re-balanced to fit, and text is wrapped to the remaining space.
For a scrollable view in a raw terminal, the README points to an example program and gives the command to run it:
cargo run --example scrollableThere is more in the examples directory than just that one. The listing includes fit-width, inputs, progress, table, lists, indented-code, content-align, high-compatibility, ask, parse-options, serialize-skin, skin-file, stderr, render-input-markdown, simple, text and text-template. The fit-width and high-compatibility names in particular suggest the areas where terminal behaviour varies, and there are two shell scripts at the repository root, fit-width-example.sh and inputs-example.sh, that sit alongside the Rust examples.
Templates keep untrusted values out of the Markdown parse
The templating side is where termimad does something a plain format! macro cannot. The README's reasoning: the format! macro is not always a good solution because you may not be sure the content is free of characters which may mess up the Markdown. A template is to Markdown what a prepared statement is to SQL, interpreted once and preventing the content from being interpreted as parts of the structure.
Inline templates use positional placeholders and a macro:
mad_print_inline!(
&skin,
"**$0 formula:** *$1*", // the markdown template, interpreted once
"Disk", // fills $0
"2*π*r", // fills $1. Note that the stars don't mess the markdown
);The listed differences from building a string and parsing it are that the Markdown parsing and template building happen only once, using once_cell internally, that the values are not interpreted as Markdown fragments and do not affect the style, and that arguments can be omitted, repeated or given in any order. The current version supports no fmt parameters and only &str arguments.
Text templates are the multi-line variant, with named placeholders written as ${name}, filled through an expander. Sub-templates handle repetitions, which is what makes them useful for lists and tables, and the set_md call is the explicit opt-in for a value that should be parsed as Markdown, while plain set is not.
Skins set colours and attributes, not the text
The skin API is where the separation of data and appearance becomes concrete. A MadSkin holds the styling, with entries for the element kinds the renderer knows about, and a caller changes the skin rather than the text. The README shows a custom skin built from the default one:
let mut skin = MadSkin::default();
skin.bold.set_fg(Yellow);
skin.paragraph.set_fgbg(Magenta, rgb(30, 30, 40));
skin.italic.add_attr(Underlined);
println!("\nand now {}\n", skin.inline("a little *too much* **style!** (and `some(code)` too)"));Inline snippets are one line or less, and print_inline renders straight to the terminal with the default skin, while skin.inline returns a string you can embed. Texts can be several lines, and tables and code blocks inside them are aligned, justified and wrapped consistently, which is the behaviour you would otherwise have to write yourself.
Colours come from the coolor crate. An example named serialize-skin suggests a skin can be written out and read back, and the skin-file example points to skins kept as files rather than in code.
Cargo metadata, versions and the gap in the README
The crate is published on crates.io as termimad, with MIT licensing, and the API documentation is on docs.rs. The package manifest gives version 0.35.5, edition 2021 and a rust-version of 1.56, which is a low minimum and useful if you need to support an older toolchain.
The dependency list is small and each entry has a job: minimad for the Markdown parsing, crossterm through coolor for styling, crokey for key combinations, crossbeam for event passing, unicode-width for correct handling of wide characters, serde for the skin serialisation, and lazy-regex and thiserror for the parsing internals. A single optional feature, special-renders, is on by default. The [patch.crates-io] block in the manifest shows commented-out path overrides for the author's own forks of coolor, crokey, minimad and lazy-regex, which is a hint about how the author tests against unreleased changes.
One inconsistency is worth flagging before you copy anything. The README's usage section still shows the dependency as termimad = "0.20", while the package version is 0.35.5. The example code in the README is therefore written for an older release than the one you would install, and the current API should be checked against the docs.rs page before you rely on it. The repository also keeps a CHANGELOG.md, a bacon.toml for the background code checker and a deny.toml for dependency licence checks, and there are no GitHub releases, so the changelog and the crates.io version history are where version differences surface.
Where termimad is the wrong tool
Three cases stand out. The first is a full-screen interactive application. The README says it plainly: use a TUI framework instead unless you have serious performance concerns or want precise control, and building on termimad means owning the event loop, the screen and the input handling yourself. The library provides crossterm and crokey as building blocks, not an application skeleton.
The second is a document that needs to be a document. Because the renderer is deliberately not a complete Markdown implementation, ordered lists, syntax colouring and links are all missing, and a help page written in Markdown with numbered steps will not render as written. The subset is chosen for terminal readability, not for fidelity to the specification, so a project whose content comes from a Markdown file authored elsewhere will hit the gaps.
The third is a team that wants rendering to be someone else's problem. Termimad is a set of utilities, it states that it is not consistent or complete, and it offers no compatibility promise beyond its changelog. A team that needs a maintained, uniform text layer across several tools may prefer a bigger abstraction, and the cost of termimad is that every application using it makes its own choices about layout, scrolling and skins.
Editorial conclusion
Adopt termimad when you are building a terminal application that needs to show Markdown-like help pages, tables and snippets with a configurable look, and when wrapping and width handling are the parts you would otherwise get wrong. Do not adopt it as a TUI framework, as a complete Markdown implementation, or for a tool that needs syntax colouring, ordered lists or links. Before you pin a version, check the crates.io page against the version in Cargo.toml, because the README's dependency snippet still shows termimad = "0.20" while the package version is 0.35.5, and read the examples directory to see which of the sample programs match the surface you need.
Frequently asked questions
Is termimad a TUI framework?
No, the README says so explicitly and advises using one of the TUI frameworks of the Rust ecosystem unless you have serious performance concerns or want precise control. Termimad provides rendering utilities and leaves the application structure to you.
Does termimad support syntax highlighting in code blocks?
No. The feature table in the README lists syntax colouring as not supported, while code blocks themselves are supported with tabs or code fences. Tables, bold, italic, inline code, horizontal rules, quotes and unordered lists are supported; ordered lists and links are not.
How do I add termimad to a Rust project?
Add it as a Cargo dependency. The README shows a termimad = "0.20" dependency line, while the package manifest lists version 0.35.5, so check the current version on crates.io before pinning it.
What do termimad templates solve?
They keep dynamic values from being parsed as Markdown. Values are substituted after the template is parsed once, so a string containing stars or backquotes cannot break the structure or change the styling. Text templates use named placeholders written as ${name} and support sub-templates for repetitions.
Which terminals does termimad handle correctly?
The project states a focus on total control and performance, with no active loop, and a striving to be correct even on Unicode and wide characters. The examples directory includes a high-compatibility example and a fit-width example, and the manifest depends on unicode-width.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/canop-termimad)Was this useful?