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

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").

    SHELLDOCS_MASK_6719ca537480

  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.

    PreviewFrame (the chrome that wraps it) renders BOTH the live component AND a reveal-on-click source view of the raw fenced snippet. Both stay mounted; the source view is CSS-hidden until you click.

  3. Inline Razor tags

    Component tags mid-markdown (<div data-shelldocs-slot="component" data-shelldocs-id="s1c2729f70987"></div>) become inline ComponentSlot markers. Same DynamicComponent rendering; no fenced wrapper, no source view. Use this for chrome-in-prose (callouts, cards, steps) as opposed to live examples.

  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.

    SHELLDOCS_MASK_f3eca6cdd4b2

    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.
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