Markdown
A computed sheet freezes into a GitHub-flavored Markdown table — one command, and results that render in any README, wiki, or docs site with no engine attached.
Everything here runs against invoice.tsvt from tsvsheet.examples:
curl -fsSO https://raw.githubusercontent.com/tsvsheet/tsvsheet.examples/main/invoice.tsvt
Freeze a sheet into a table
One engine, one grid, many renderings — --format md (alias markdown) serializes the computed grid as a GitHub-flavored pipe table. The =formula cells are gone; only their values remain:
$ tsv render invoice.tsvt --format md
| Item | Qty | Price | Amount |
| --- | --- | --- | --- |
| Widget | 3 | 4.50 | 13.5 |
| Gadget | 2 | 12.00 | 24 |
| Gizmo | 5 | 2.25 | 11.25 |
| Sprocket | 10 | 0.99 | 9.9 |
| Total | | | 58.65 |Paste that into a README, a wiki page, or an issue and it renders anywhere Markdown does — no engine, no styling, no JavaScript. Each cell is escaped for the table: a literal | is backslashed so it never opens a new column, and an in-cell newline (a CHAR(10)) becomes a <br> so it never splits the row.
The sheet is optional — omit it and render reads stdin, so a table drops straight into a pipeline:
cat invoice.tsvt | tsv render --format md
Bake sheets inside a document
For a docs site, the sheet lives in the Markdown as a fenced sheet block, and the @tsvsheet/tsvsheet-remark remark host rewrites each block into a computed table at build time. Point it at any document — here one built straight from the example:
{ printf '# Order 1042\n\n```sheet\n'; cat invoice.tsvt; printf '\n```\n'; } > order.mdWith output: "markdown", the plugin swaps every sheet block for its computed pipe table, and remark-stringify emits portable Markdown — the same bytes the CLI produces:
import { readFile } from "node:fs/promises";
import { unified } from "unified";
import remarkParse from "remark-parse";
import remarkStringify from "remark-stringify";
import { remarkTsvsheet } from "@tsvsheet/tsvsheet-remark";
const doc = await readFile(process.argv[2], "utf8");
const out = await unified()
.use(remarkParse)
.use(remarkTsvsheet, { output: "markdown" })
.use(remarkStringify)
.process(doc);
process.stdout.write(String(out));
$ node bake.mjs order.md # Order 1042 | Item | Qty | Price | Amount | | --- | --- | --- | --- | | Widget | 3 | 4.50 | 13.5 | | Gadget | 2 | 12.00 | 24 | | Gizmo | 5 | 2.25 | 11.25 | | Sprocket | 10 | 0.99 | 9.9 | | Total | | | 58.65 |
Commit order.md as the source of truth and bake it on publish; the tracked file stays diffable text, and every reader gets a computed table. Drop the output option and the same block renders as a live HTML <table> instead — the remark and goldmark hosts share one contract, so a .tsvt block renders identically through the CLI, a Go pipeline, or a JavaScript one.