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.1-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").SHELLDOCS_MASK_6719ca537480
- 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.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. - Inline Razor tags
Component tags mid-markdown (
<div data-shelldocs-slot="component" data-shelldocs-id="s1c2729f70987"></div>) become inlineComponentSlotmarkers. Same DynamicComponent rendering; no fenced wrapper, no source view. Use this for chrome-in-prose (callouts, cards, steps) as opposed to live examples. - 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.SHELLDOCS_MASK_f3eca6cdd4b2
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. |
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].