TagBites.Text.Markdown 2.2.0

Prefix Reserved
dotnet add package TagBites.Text.Markdown --version 2.2.0
                    
NuGet\Install-Package TagBites.Text.Markdown -Version 2.2.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="TagBites.Text.Markdown" Version="2.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="TagBites.Text.Markdown" Version="2.2.0" />
                    
Directory.Packages.props
<PackageReference Include="TagBites.Text.Markdown" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add TagBites.Text.Markdown --version 2.2.0
                    
#r "nuget: TagBites.Text.Markdown, 2.2.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package TagBites.Text.Markdown@2.2.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=TagBites.Text.Markdown&version=2.2.0
                    
Install as a Cake Addin
#tool nuget:?package=TagBites.Text.Markdown&version=2.2.0
                    
Install as a Cake Tool

TagBites.Text.Markdown

Nuget .NET Standard 2.0 License Downloads

TagBites.Text.Markdown is a C# library for programmatically building Markdown documents. A document is a tree of typed elements. The generated output follows CommonMark, supports GitHub Flavored Markdown tables, task lists, and some Markdig extensions.

Try it online - paste Markdown and read the C# code that rebuilds it.

Install

dotnet add package TagBites.Text.Markdown

Targets netstandard2.0. No dependencies.

Usage

var doc = new MarkdownDocument();
doc.AddHeader(1, "TagBites.Expressions");
doc.AddHeader(2, "Options");

doc.AddParagraph("Every option is set on ExpressionParserOptions.");

doc.AddTable()
    .SetHeaders("Option", "Purpose")
    .WithRow("Parameters", "names the inputs")
    .WithRow("StaticImports", "acts like using static");

doc.AddParagraph("What the parser accepts:");

doc.AddList()
    .WithCheckItem(true, "operators and precedence")
    .WithCheckItem(true, "pattern matching and tuples")
    .WithCheckItem(true, "lambdas and LINQ")
    .WithCheckItem(false, "statements");

var markdown = doc.ToString();

ToString returns the whole document. WriteTo sends it to a Stream or a TextWriter as it is produced, so a large document never has to fit in memory:

using var file = File.Create("readme.md");
doc.WriteTo(file);

A stream receives UTF-8 without a byte order mark and stays open.

Output:

# TagBites.Expressions

## Options

Every option is set on ExpressionParserOptions.

| Option        | Purpose                |
| ------------- | ---------------------- |
| Parameters    | names the inputs       |
| StaticImports | acts like using static |

What the parser accepts:

- [x] operators and precedence
- [x] pattern matching and tuples
- [x] lambdas and LINQ
- [ ] statements

Elements

  • headers (AddHeader), with optional custom id
  • paragraphs (AddParagraph)
  • code blocks (AddCode), with optional language
  • quotes (AddQuote), multiline and nestable
  • unordered lists (AddList)
  • ordered lists (AddList(isOrdered: true))
  • task lists (AddCheckItem), a check box on any list item
  • tables (AddTable), with padded columns, column alignment and cell escaping
  • thematic breaks (AddThematicBreak)
  • raw HTML blocks (AddHtml)

MarkdownDocument, MarkdownSection, MarkdownQuote and MarkdownListItem hold any block element. MarkdownList holds items and MarkdownTable holds cells. Every other element is a leaf.

Method prefixes:

Prefix Effect Returns
Add* Appends a new element The new element
With* Appends a new element The same object
Set* Replaces a value The same object

Add* goes one level deeper, With* and Set* stay put, so a whole document fits in one expression:

var doc = new MarkdownDocument()
    .WithHeader(1, "Title")
    .WithParagraph("Intro.")
    .WithElement(new MarkdownList()
        .WithItem("a")
        .WithItem("b"));

There is no WithList or WithTable, because it would produce an empty element. So build those first and pass them as argument to WithElement.

Sections

A section is a header plus everything under it, and the level comes from the nesting:

var root = doc.AddSection("TagBites.Text.Markdown");
root.AddParagraph("C# library for building Markdown.");

var usage = root.AddSection("Usage");
usage.AddParagraph("Install it and start.");

var tables = usage.AddSection("Tables");
tables.AddParagraph("...");

Output:

# TagBites.Text.Markdown

C# library for building Markdown.

## Usage

Install it and start.

### Tables

...

A section writes its own header, so nest another section rather than adding a header to it.

A level can be forced using an overload:

parent.AddSection(3, "Details");

Past level six Markdown has no header, and a deeper section falls back to bold text with a hard line break:

###### Level six

**Level seven**  
Content of the seventh level.

An explicit anchor comes from SetCustomId:

section.SetCustomId("custom-id"); // ## <a id="custom-id"></a> Some section

MarkdownFormat.HeaderAnchorStyle switches that to {#custom-id}.

To link a section, pass it to MarkdownText.Link instead of writing the anchor twice:

var section = root.AddSection("Command line options");

root.AddParagraph("See " + MarkdownText.Link(section) + " below.");
// See [Command line options](#command-line-options) below.

The address is AnchorId, which is CustomId when one is set and otherwise the header text in lower case with hyphens (like in GitHub).

Text and escaping

Every element takes a MarkdownText. A string you pass converts implicitly and is escaped, so text from an untrusted source cannot introduce markup:

doc.AddParagraph("Report by [admin](https://link.example) **now**");
// Report by \[admin\](https://link.example) \*\*now\*\*

Escaping is minimal. A character is escaped where it would change the parse and left alone where it would not:

doc.AddParagraph("TagBites.Expressions accepts digit separators like 1_000_000 and compiles to Func<>");
// TagBites.Expressions accepts digit separators like 1_000_000 and compiles to Func<>

Content that is already Markdown goes through MarkdownText.Raw. The inline builders return raw content too:

MarkdownText.Bold("text");                    // **text**
MarkdownText.Italic("text");                  // _text_
MarkdownText.Strikethrough("text");           // ~~text~~
MarkdownText.Code("var x;");                  // `var x;`
MarkdownText.Link("name", "https://x.com");   // [name](https://x.com)
MarkdownText.Link("name", "x.md", "Tooltip"); // [name](x.md "Tooltip")
MarkdownText.Image("logo", "logo.png");       // ![logo](logo.png)
MarkdownText.LineBreak;                       // two spaces and a new line

Combine with +:

var text = MarkdownText.Bold("total") + " for [all] items";
// text.Markdown -> **total** for \[all\] items
// text.Text     -> total for [all] items

The plain text mode returns Text.

Tables

A cell holds inline content, so bold text, links and images go in as text:

table.SetHeaders("name", "docs")
    .WithRow(MarkdownText.Bold("total"), MarkdownText.Link("guide", "x.md"));
| name      | docs          |
| --------- | ------------- |
| **total** | [guide](x.md) |

Alignment comes from SetAlignments, or from WithHeader one column at a time:

table.SetHeaders("left", "center", "right")
    .SetAlignments(
        MarkdownTableColumnAlignment.Left,
        MarkdownTableColumnAlignment.Center,
        MarkdownTableColumnAlignment.Right)
    .WithRow("a", "b", "c");
| left | center | right |
| :--- | :----: | ----: |
| a    | b      | c     |

Format

Rendering options live on MarkdownFormat:

Property Meaning
Output Markdown or PlainText.
IgnoredElementTypes Element types (including derived) left out of the output, together with their content.
HeaderAnchorStyle HtmlAnchor for <a id="id"></a>, Attribute for {#id}.
SeparateLooseListItems Whether a blank line separates the items of a loose list.

Whole element types can be left out, which gives a description without the code that goes with it:

var format = new MarkdownFormat
{
    Output = MarkdownOutputKind.PlainText,
    IgnoredElementTypes = { typeof(MarkdownCode) }
};

doc.ToString(format);

Plain text output strips the syntax: headers, quotes and code blocks keep their text, lists lose their markers, tables come out as space-separated rows. A checkbox outputs as or . Ignoring MarkdownCode removes code blocks and keeps a code span inside a sentence.

var plain = MarkdownFormat.PlainText;

new MarkdownHeader(1, "Title").ToString(plain);                     // Title
new MarkdownCode("csharp", "var x;").ToString(plain);               // var x;
new MarkdownListItem("task") { IsChecked = true }.ToString(plain);  // ☑ task

The format freezes the first time it is used for writing. A later change throws InvalidOperationException.

Front matter

var doc = new MarkdownDocument
{
    FrontMatter = new MarkdownFrontMatter
    {
        Title = "Release notes",
        Description = "What changed in this version.",
        ["date"] = "2026-08-01"
    }
};

doc.FrontMatter.SetValues("tags", "markdown", "builder");

var notes = doc.AddSection("Release notes");
notes.AddParagraph("First public version.");

Output:

---
title: Release notes
description: What changed in this version.
date: 2026-08-01
tags: [markdown, builder]
---

# Release notes

First public version.

Standards

The output follows CommonMark and the GitHub Flavored Markdown extensions the model exposes: tables, task lists and strikethrough. Every construct is parsed back with Markdig in the test suite and has to produce the same document.

Limitations

  • The library builds Markdown, it does not parse it. If you need to read Markdown, use Markdig.
  • Escaping keeps text inside its block. A backslash cannot escape white space, so leading indentation and a blank line come out as the &#32; entity instead.
  • Table cells hold inline content only, which is all the GitHub Flavored Markdown spec allows.
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.2.0 48 8/6/2026
2.1.0 78 8/5/2026
2.0.0 113 8/2/2026
1.0.0 2,465 5/29/2024