Markdig
| Language | C# (.NET Standard 2.0+, .NET 8+) |
|---|---|
| License | BSD-2-Clause |
| Repository | github.com/xoofx/markdig |
| Documentation | xoofx.github.io/markdig |
| Depends on | nothing |
| Read / write | parses CommonMark plus extensions; renders HTML, plain text and normalized Markdown |
Markdig parses Markdown into a MarkdownDocument tree and renders it to HTML. Without options it follows CommonMark 0.31.2 exactly. A MarkdownPipelineBuilder adds extensions: UseAdvancedExtensions() turns on most of them (pipe and grid tables, task lists, footnotes, auto identifiers, alerts, emphasis extras, figures, math, diagrams, …). It’s fast, allocates little, and keeps source positions, so it also works for editors and linters. A roundtrip mode keeps all whitespace for lossless edits.
Raw HTML passes through by default, as CommonMark requires. For untrusted input, call DisableHtml() or sanitize the output.
Installation#
dotnet add package Markdig
Basic usage: convert Markdown to HTML#
using System;
using System.IO;
using Markdig;
var pipeline = new MarkdownPipelineBuilder()
.UseAdvancedExtensions() // tables, task lists, footnotes, auto ids, ...
.Build();
string markdown = File.ReadAllText("README.md");
string html = Markdown.ToHtml(markdown, pipeline);
File.WriteAllText("README.html", html);
Console.WriteLine(html.Length);
Markdown.ToHtml(markdown) without a pipeline gives plain CommonMark.
How to enable only GitHub Flavored Markdown features#
var pipeline = new MarkdownPipelineBuilder()
.UsePipeTables()
.UseTaskLists()
.UseEmphasisExtras() // ~~strikethrough~~ (and ~sub~, ^sup^, ++ins++, ==mark==)
.UseAutoLinks() // bare www. and https:// links
.UseFootnotes()
.Build();
How to list headings and links#
using System.Linq;
using Markdig;
using Markdig.Syntax;
using Markdig.Syntax.Inlines;
MarkdownDocument doc = Markdown.Parse(markdown, pipeline);
foreach (HeadingBlock h in doc.Descendants<HeadingBlock>())
{
string text = string.Concat(h.Inline!.Descendants<LiteralInline>().Select(l => l.Content.ToString()));
Console.WriteLine($"h{h.Level} (line {h.Line + 1}): {text}");
}
foreach (LinkInline link in doc.Descendants<LinkInline>())
Console.WriteLine($"{(link.IsImage ? "image" : "link")}: {link.Url}");
How to read YAML front matter#
using Markdig.Extensions.Yaml;
var pipeline = new MarkdownPipelineBuilder().UseYamlFrontMatter().Build();
MarkdownDocument doc = Markdown.Parse(markdown, pipeline);
YamlFrontMatterBlock? fm = doc.Descendants<YamlFrontMatterBlock>().FirstOrDefault();
if (fm != null)
{
string yaml = fm.Lines.ToString(); // pass to a YAML library such as YamlDotNet
Console.WriteLine(yaml);
}
string html = doc.ToHtml(pipeline); // front matter is not rendered
How to convert Markdown to plain text#
string text = Markdown.ToPlainText("# Title\n\nSome **bold** text and a [link](https://example.com).");
// markup removed: "Title", "Some bold text and a link."
How to render untrusted Markdown safely#
var safe = new MarkdownPipelineBuilder()
.UseAdvancedExtensions()
.DisableHtml() // raw HTML is escaped, not rendered
.Build();
string html = Markdown.ToHtml(userInput, safe);
DisableHtml doesn’t filter javascript: URLs in links; run the HTML through a sanitizer such as HtmlSanitizer for full protection.
Tips#
- Build a
MarkdownPipelineonce and reuse it; it’s thread-safe. UseAutoIdentifiers()(part of the advanced set) addsidattributes to headings for anchor links.Markdown.Normalize(markdown)reformats a document into consistent Markdown.