ShellDocs.Markdown

ShellDocs.Markdown turns a .md file into a RenderedDocument : the object MarkdownContent uses to render prose + live components on a page. It's the shim between raw markdown and Blazor.

You rarely touch this package directly

It comes in transitively via ShellDocs.Components. You reference it explicitly only when you're building custom pages that render markdown outside DocsPage, or writing a Markdig extension.

Install

bash
dotnet add package ShellDocs.Markdown --prerelease

What it does

The pipeline in one paragraph: read the .md file → parse YAML frontmatter → walk Markdig's syntax tree → find ```razor:preview fences and inline <Component> tags → replace them with placeholder slots → hand the rendered HTML + slot metadata back to MarkdownContent, which swaps the placeholders for real <DynamicComponent> calls at runtime.

  1. Frontmatter parsing

    YAML at the top of every .md file, delimited by ---. Parsed via YamlDotNet, surfaced as RenderedDocument.Frontmatter.GetValue<T>("key").

    markdown
    ---
    title: My Page
    description: Short summary.
    order: 3
    ---
    
  2. razor:preview fences

    A ```razor:preview fenced code block gets treated as a live component demo. The parser extracts the target component + its attribute values + any child content, then emits a PreviewSlot marker.

    The fence body is parsed as Razor into a small node tree (components, HTML elements, text), so several sibling tags and wrapper elements render exactly as written. PreviewFrame wraps the result with Preview and Code tabs; the Code tab shows the fence body unchanged.

  3. Inline Razor tags

    Component tags mid-markdown (<Callout Variant="info">…</Callout>) become inline ComponentSlot markers. Same DynamicComponent rendering; no frame, no Code tab. Their bodies are rendered as markdown. Tags inside code spans and fences are left alone.

  4. Per-property type coercion

    Attribute values in markdown are all strings : but Blazor's [Parameter] properties are typed. SlotRenderer reflects on the target component's props, coerces "true" → bool, "2" → int, "info" → enum, before passing the parameter dictionary to DynamicComponent.

    markdown
    <FileTreeItem Name="src" IsFolder="true" Highlight="false" />
    

    That IsFolder="true" is a real bool true by the time FileTreeItem receives it : no runtime cast exception.

Key types

Prop Type Default Description
MarkdownRenderer service — Injected via DI. Render(string markdown) and RenderFile(string path) return RenderedDocument.
RenderedDocument record — Rendered HTML + slot list + parsed source + extracted headings + frontmatter access.
TypeRegistry service — Maps markdown tag names to Blazor component types. Populated from the ShellDocsOptions registration calls. Built-ins are also registered under a Docs prefix, and collisions are recorded.
SlotExtractor internal — Walks the parsed markdown, extracts razor:preview and inline-tag slots, replaces them with placeholder markers.
FrontmatterParser static — YAML frontmatter parser via YamlDotNet. Handles the leading ---…--- delimiter.
MarkdownPlainText static — Extracts plain-text prose from markdown (strips frontmatter / fences / HTML / Razor tags / images / links / inline code / emphasis / heading #). Used by SearchIndex for body-text indexing.

Registering your own components for markdown authors

AddShellDocs auto-registers every shipped primitive. To let authors reference your components too:

csharp
builder.Services.AddShellDocs(o =>
{
    // Individual registration
    o.RegisterComponent<MyBrand.Callout>();
    o.RegisterComponent<MyBrand.Button>("Btn");   // aliased tag

    // Or the assembly-scan one-liner (recommended for your own component libraries)
    o.RegisterComponentsFromAssembly<MyBrand.Button>();
});

The scan picks up every public non-abstract ComponentBase subclass in the marker's assembly. Filter it if you want a namespace subset:

csharp
o.RegisterComponentsFromAssembly(typeof(MyBrand.Button).Assembly,
    t => t.Namespace?.StartsWith("MyBrand.Components") == true);

Components that should NOT be markdown-authorable (internal render machinery, layouts) opt out with [ShellDocsIgnore].

Learn more