![]() |
VOOZH | about |
dotnet add package Mdazor --version 0.0.0-alpha.0.17
NuGet\Install-Package Mdazor -Version 0.0.0-alpha.0.17
<PackageReference Include="Mdazor" Version="0.0.0-alpha.0.17" />
<PackageVersion Include="Mdazor" Version="0.0.0-alpha.0.17" />Directory.Packages.props
<PackageReference Include="Mdazor" />Project file
paket add Mdazor --version 0.0.0-alpha.0.17
#r "nuget: Mdazor, 0.0.0-alpha.0.17"
#:package Mdazor@0.0.0-alpha.0.17
#addin nuget:?package=Mdazor&version=0.0.0-alpha.0.17&prereleaseInstall as a Cake Addin
#tool nuget:?package=Mdazor&version=0.0.0-alpha.0.17&prereleaseInstall as a Cake Tool
A Markdig extension that lets you embed Razor components directly in Markdown for server-side rendering.
Ever wanted to write Markdown like this:
# My Documentation
Here's some regular markdown content.
<AlertCard type="warning">
This is a **real Razor component** with Markdown content inside!
- It supports lists
- And *formatting*
- And even nested components!
<Button variant="primary">Click me!</Button>
</AlertCard>
More regular markdown continues here...
And have it actually render real Razor components server-side? That's what this does.
Writing documentation with static Markdown is fine, but sometimes you want to:
This is a custom Markdig extension that:
<ComponentName prop="value">content</ComponentName> syntax in your MarkdownThe magic happens in the parsing phase - we intercept component-looking tags before they get processed as regular HTML, instantiate actual Razor components, and render them using Blazor's server-side rendering to produce static HTML output.
Your components just need to be normal Razor components:
@{
var alertClass = Type switch
{
"success" => "alert-success",
"warning" => "alert-warning",
"danger" => "alert-danger",
"info" => "alert-info",
_ => "alert-primary"
};
}
<div class="alert @alertClass" role="alert">
@ChildContent
</div>
@code {
[Parameter] public string Type { get; set; } = "primary";
[Parameter] public RenderFragment? ChildContent { get; set; }
}
The ChildContent parameter gets populated with the processed Markdown content from inside the component tags.
<Icon name="star" size="24" />
<Card title="Hello World">
This content becomes the component's ChildContent parameter.
</Card>
<AlertBox type="info">
## This is a heading inside a component
And here's a nested component:
<Button onclick="alert('hi')">Click me</Button>
- Lists work too
- **Bold text**
- Everything you'd expect
</AlertBox>
If a component isn't registered, it just renders as HTML:
<SomeUnknownWidget foo="bar">content</SomeUnknownWidget>
Becomes: <someunknownwidget foo="bar">content</someunknownwidget> with the error message as an HTML comment.
services.AddMdazor()
.AddMdazorComponent<AlertCard>()
.AddMdazorComponent<Button>()
.AddMdazorComponent<Icon>();
Create your own pipeline, ensuring to inject your service provider so we can find the components:
var pipeline = new MarkdownPipelineBuilder()
.UseMdazor(serviceProvider)
.Build();
var html = Markdown.ToHtml(markdownContent, pipeline);
For advanced scenarios, you can still use the renderer directly:
var document = Markdown.Parse(markdownContent, pipeline);
using var writer = new StringWriter();
var renderer = new BlazorRenderer(writer, componentRegistry, serviceProvider);
renderer.Render(document);
var html = writer.ToString();
Sometimes a component needs to know something about where it's being rendered — the current file name, the page route, front matter — that isn't written as a tag attribute. You can hand a bag of arbitrary host values to every component rendered from a document.
Set it on Markdig's MarkdownParserContext and pass that to Markdown.ToHtml/Markdown.Parse:
var context = new MarkdownParserContext()
.SetMdazorContext(new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
{
["CurrentFile"] = "guide/intro.md",
["Route"] = currentRoute,
});
var html = Markdown.ToHtml(markdown, pipeline, context);
A component reads it as an ambient cascading value — no plumbing on the tag:
@code {
[CascadingParameter] public MdazorContext? Context { get; set; }
private string? File => Context?.Get<string>("CurrentFile");
}
MdazorContext offers Values, an indexer (Context["CurrentFile"]), TryGet, and
Get<T>. The bag is delivered as a CascadingValue<MdazorContext>, so it reaches a
component and its descendants.
For the interactive render-tree path (<MdazorContent>), pass an MdazorContext directly:
<MdazorContent Markdown="@markdown" Context="@MdazorContext" />
@code {
private MdazorContext MdazorContext { get; } = new(new Dictionary<string, object?>
{
["CurrentFile"] = "guide/intro.md",
});
}
Cascading values do not cross a Blazor render-mode boundary, so the context is not automatically delivered to an interactive (WASM/Server) island. It is available on the static string path and the prerendered/server render tree.
HtmlRenderer for actual server-side component rendering into static HTML@inject works inside components, so you can inject services as usualIt doesn't handle whitespace all that great. If you are using a markdown element that relies on precise whitespace such as a code element or blockquote, don't indent your tags e.g.
Do this:
<Card>
```csharp
var i = 2 + 2;
```
</Card>
Not this:
<Card>
```csharp
var i = 2 + 2;
```
</Card>
Server-side rendering only (components render to static HTML)
Components need to be registered ahead of time
No support for complex parameter types (just strings, numbers, bools for now)
Component names must start with a capital letter (follows HTML custom element rules)
Mdazor/ - The main library with the Markdig extensionMdazor.Demo/ - A Blazor Server app showing it in actionMdazor.Tests/ - Unit and integration testscd Mdazor.Demo
dotnet run
Then check out the demo pages to see components embedded in Markdown.
This is a skunkworks project, so don't expect enterprise-level polish. But if you find bugs or have ideas, PRs welcome!
The main areas that could use work:
MIT - do whatever you want with it.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net9.0 net9.0 is compatible. net9.0-android net9.0-android was computed. net9.0-browser net9.0-browser was computed. net9.0-ios net9.0-ios was computed. net9.0-maccatalyst net9.0-maccatalyst was computed. net9.0-macos net9.0-macos was computed. net9.0-tvos net9.0-tvos was computed. net9.0-windows net9.0-windows was computed. net10.0 net10.0 was computed. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
Showing the top 2 NuGet packages that depend on Mdazor:
| Package | Downloads |
|---|---|
|
MyLittleContentEngine
A powerful content engine for building static sites with Blazor and ASP.NET Core |
|
|
Pennington
Content engine for .NET with Markdown processing, front matter, and static site generation |
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.0.0-alpha.0.17 | 282 | 6/8/2026 |
| 0.0.0-alpha.0.16 | 56 | 5/28/2026 |
| 0.0.0-alpha.0.14 | 463 | 4/28/2026 |
| 0.0.0-alpha.0.13 | 781 | 11/16/2025 |
| 0.0.0-alpha.0.12 | 140 | 11/15/2025 |
| 0.0.0-alpha.0.10 | 521 | 7/16/2025 |
| 0.0.0-alpha.0.9 | 523 | 7/3/2025 |
| 0.0.0-alpha.0.8 | 208 | 6/23/2025 |
| 0.0.0-alpha.0.7 | 213 | 6/16/2025 |
| 0.0.0-alpha.0.6 | 166 | 6/16/2025 |
| 0.0.0-alpha.0.5 | 160 | 6/16/2025 |
| 0.0.0-alpha.0.4 | 164 | 6/16/2025 |
| 0.0.0-alpha.0.3 | 155 | 6/16/2025 |
| 0.0.0-alpha.0.1 | 185 | 6/16/2025 |
| 0.0.0-alpha.0 | 156 | 6/16/2025 |