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.
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
<PackageReference Include="ShellDocs.Markdown" Version="0.1.17-alpha" />
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.
- Frontmatter parsing
YAML at the top of every
.mdfile, delimited by---. Parsed via YamlDotNet, surfaced asRenderedDocument.Frontmatter.GetValue<T>("key").markdown--- title: My Page description: Short summary. order: 3 --- - razor:preview fences
A
```razor:previewfenced code block gets treated as a live component demo. The parser extracts the target component + its attribute values + any child content, then emits aPreviewSlotmarker.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.
PreviewFramewraps the result with Preview and Code tabs; the Code tab shows the fence body unchanged. - Inline Razor tags
Component tags mid-markdown (
<Callout Variant="info">…</Callout>) become inlineComponentSlotmarkers. Same DynamicComponent rendering; no frame, no Code tab. Their bodies are rendered as markdown. Tags inside code spans and fences are left alone. - Per-property type coercion
Attribute values in markdown are all strings : but Blazor's
[Parameter]properties are typed.SlotRendererreflects on the target component's props, coerces"true"→bool,"2"→int,"info"→ enum, before passing the parameter dictionary toDynamicComponent.markdown<FileTreeItem Name="src" IsFolder="true" Highlight="false" />That
IsFolder="true"is a realbool trueby the timeFileTreeItemreceives 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:
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:
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].