MarkdownToRazor 2.0.2

There is a newer version of this package available.
See the version list below for details.
dotnet add package MarkdownToRazor --version 2.0.2
                    
NuGet\Install-Package MarkdownToRazor -Version 2.0.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="MarkdownToRazor" Version="2.0.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="MarkdownToRazor" Version="2.0.2" />
                    
Directory.Packages.props
<PackageReference Include="MarkdownToRazor" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add MarkdownToRazor --version 2.0.2
                    
#r "nuget: MarkdownToRazor, 2.0.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package MarkdownToRazor@2.0.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=MarkdownToRazor&version=2.0.2
                    
Install as a Cake Addin
#tool nuget:?package=MarkdownToRazor&version=2.0.2
                    
Install as a Cake Tool

MarkdownToRazor

.NET License: MIT NuGet GitHub Packages

Transform your Markdown files into beautiful Blazor pages with automatic routing and syntax highlighting.

MarkdownToRazor is a powerful .NET library that bridges the gap between Markdown content and Blazor applications. Whether you're building documentation sites, blogs, or content-driven applications, this library provides everything you need to seamlessly integrate Markdown into your Blazor projects.

โœจ What Can You Do?

  • ๐Ÿ“ Runtime Rendering: Display markdown content dynamically in your Blazor components
  • ๐Ÿ—๏ธ Build-Time Generation: Automatically convert markdown files to Razor pages during compilation
  • ๐Ÿงญ Dynamic UI Generation: Build navigation menus and content browsers with page discovery service
  • ๐ŸŽจ Beautiful Styling: Integrated with Microsoft FluentUI design system
  • ๐Ÿ’ก Syntax Highlighting: Code blocks with highlight.js integration and copy-to-clipboard functionality
  • ๐Ÿ”— Automatic Routing: Generate routable pages from your markdown files with YAML frontmatter or HTML comment configuration support
  • ๐Ÿ“ Flexible Content: Load from files, URLs, or provide inline markdown content

๐Ÿ†• What's New in v2.0.1 - QUALITY RELEASE

๐Ÿ”ง Enhanced Package Quality - Professional-grade NuGet package with debugging support!

โœจ Package Quality Improvements

Source Link & Debugging:

  • โœ… Source Link Integration - "Go to Definition" now works with original source code
  • โœ… Embedded Debug Symbols - Enhanced debugging experience for package consumers
  • โœ… Symbol Packages (.snupkg) - Automatic symbol package generation for NuGet.org
  • โœ… Deterministic Builds - Reproducible packages across different build environments

Build & CI Enhancements:

  • โœ… Continuous Integration Build Flags - Optimized for CI/CD environments
  • โœ… Enhanced Package Validation - Meets industry best practices
  • โœ… Cross-Platform Path Handling - Improved reliability across Windows/Linux/macOS
  • โœ… Comprehensive Test Coverage - 22+ passing tests covering all scenarios

๐Ÿ†• What's New in v2.0.0 - MAJOR RELEASE

๐ŸŽฏ Single Unified Package - We've consolidated everything into one powerful package!

๐Ÿ”ฅ BREAKING CHANGES - Migration Required

Package Consolidation:

  • Old: 3 separate packages (MDFileToRazor.Components, MDFileToRazor.CodeGeneration, MDFileToRazor.MSBuild)
  • New: Single MarkdownToRazor package with everything included!

Modernized Naming:

  • Package: MDFileToRazor โ†’ MarkdownToRazor
  • Namespaces: MDFileToRazor.* โ†’ MarkdownToRazor.*
  • Services: AddMarkdownToRazorServices() โ†’ AddMarkdownToRazorServices()

Framework Support:

  • โœ… Added .NET 9.0 support
  • โœ… Maintained .NET 8.0 support
  • โŒ Removed .NET Standard 2.1 (incompatible with Blazor)

๐Ÿ”„ Quick Migration

// Before v2.0
builder.Services.AddMarkdownToRazorServices("../content");

// After v2.0+ (including v2.0.1)
builder.Services.AddMarkdownToRazorServices("../content");

๐Ÿ› ๏ธ Enhanced Path Handling & File Discovery

// Get file-to-route mapping for dynamic navigation
var fileRoutes = await MdFileService.DiscoverMarkdownFilesWithRoutesAsync();
foreach (var (fileName, route) in fileRoutes)
{
    Console.WriteLine($"File: {fileName} โ†’ Route: {route}");
}

๐Ÿ“ Flexible Source Directory Configuration

// Relative paths from project root
builder.Services.AddMarkdownToRazorServices("content/docs");

// Multiple folders up (perfect for shared content)
builder.Services.AddMarkdownToRazorServices("../../../SharedDocumentation");

// Absolute paths (cross-project content sharing)
builder.Services.AddMarkdownToRazorServices(@"C:\SharedContent\ProjectDocs");

// Project root directory
builder.Services.AddMarkdownToRazorServices(".");

๐Ÿงช Comprehensive Test Coverage

  • 22 passing tests covering all scenarios
  • Cross-platform path handling with proper normalization
  • Edge case coverage for various directory structures

๐Ÿ†• What's New in v1.1.0

โœจ IGeneratedPageDiscoveryService - Programmatically discover and work with your generated Razor pages!

// Inject the service into your components
@inject IGeneratedPageDiscoveryService PageDiscovery

// Get all pages with metadata
var pages = await PageDiscovery.GetAllPagesAsync();

// Filter by tags
var blogPosts = await PageDiscovery.GetPagesByTagAsync("blog");

// Find pages by route pattern
var apiDocs = await PageDiscovery.GetPagesByRoutePatternAsync("/api/*");

// Build dynamic navigation menus
foreach (var page in pages)
{
    Console.WriteLine($"Route: {page.Route}, Title: {page.Title}");
}

Perfect for building:

  • ๐Ÿ“‹ Dynamic sitemaps from your content
  • ๐Ÿงญ Automatic navigation menus that update as you add pages
  • ๐Ÿท๏ธ Tag-based content filtering and organization
  • ๐Ÿ“Š Content management dashboards with page metadata

๐Ÿ“ฆ Installation

Single package with everything included!

NuGet.org (Stable Releases)

dotnet add package MarkdownToRazor

NuGet Version NuGet Downloads

GitHub Packages (Pre-release & Latest)

dotnet add package MarkdownToRazor --source https://nuget.pkg.github.com/DavidH102/index.json

๐Ÿš€ Quick Start

This is the primary use case - automatically convert markdown files to routable Blazor pages during build:

# Single package with all features included
dotnet add package MarkdownToRazor

Two approaches for build-time generation:

Option A: MSBuild Integration (Automatic)

Add to your .csproj:

<PropertyGroup>
  <MarkdownSourceDirectory>$(MSBuildProjectDirectory)\content</MarkdownSourceDirectory>
  <GeneratedPagesDirectory>$(MSBuildProjectDirectory)\Pages\Generated</GeneratedPagesDirectory>
</PropertyGroup>

<Target Name="GenerateMarkdownPages" BeforeTargets="Build">
  <Exec Command="dotnet run --project path/to/MarkdownToRazor.CodeGeneration -- &quot;$(MarkdownSourceDirectory)&quot; &quot;$(GeneratedPagesDirectory)&quot;" />
</Target>
Option B: Manual Code Generation
# Run code generation manually
cd CodeGeneration
dotnet run -- "../content" "../Pages/Generated"

Service Registration for Dynamic Navigation:

Even for build-time scenarios, you often want service registration to build dynamic navigation menus from discovered routes:

using MarkdownToRazor.Components.Extensions;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();

// Register services to enable dynamic UI generation from discovered routes
builder.Services.AddMarkdownToRazorServices(options =>
{
    options.SourceDirectory = "content"; // Where your markdown files are located
    options.BaseRoutePath = "/docs"; // Optional route prefix for generated pages
    // OutputDirectory NOT needed - files are generated at build-time, not runtime
});

var app = builder.Build();
// ... rest of configuration

Dynamic Navigation Example:

@inject IMdFileDiscoveryService MdFileDiscovery

<FluentNavGroup Title="Documentation" Icon="@(new Icons.Regular.Size24.DocumentText())">
    @{
        var documentationRoutes = await MdFileDiscovery.DiscoverMarkdownFilesWithRoutesAsync();
        var orderedFiles = documentationRoutes.OrderBy(kvp => GetFileDisplayName(kvp.Key)).ToList();
    }

    @foreach (var (filename, route) in orderedFiles)
    {
        var displayName = GetFileDisplayName(filename);
        <FluentNavLink Href="@route" Match="NavLinkMatch.All"
                       Icon="@GetDocumentationIcon(displayName)">
            @displayName
        </FluentNavLink>
    }
</FluentNavGroup>

2. Runtime Markdown Rendering (Advanced Use Case)

For displaying dynamic markdown content without generating pages:

Your Blazor Page:

@page "/docs"
@using MarkdownToRazor.Components
@inject IMdFileDiscoveryService MdFileDiscovery

<MarkdownSection Content="@markdownContent" />


<MarkdownFileExplorer />

@code {
    private string markdownContent = @"
# Welcome to My Documentation

This is **bold text** and this is *italic text*.

```cs
public class Example
{
    public string Name { get; set; } = ""Hello World"";
}
```

";
    }
}
```

### 2. Build-Time Page Generation

Automatically convert markdown files to routable Blazor pages:

```bash
dotnet add package MarkdownToRazor

Create markdown files with YAML frontmatter:

content/about.md:

---
title: About Us
route: /about
layout: MainLayout
---

# About Our Company

We build amazing software...

Or use HTML comment configuration (new in v1.2.0):

content/about.md:






# About Our Company

We build amazing software...

๐Ÿ’ก Tip: HTML comment configuration takes precedence over YAML frontmatter when both are present. This provides flexibility for different authoring preferences and tool compatibility.

Add to your .csproj:

<PropertyGroup>
  <MarkdownSourceDirectory>$(MSBuildProjectDirectory)\content</MarkdownSourceDirectory>
  <GeneratedPagesDirectory>$(MSBuildProjectDirectory)\Pages\Generated</GeneratedPagesDirectory>
</PropertyGroup>

<Target Name="GenerateMarkdownPages" BeforeTargets="Build">
  <Exec Command="dotnet run --project path/to/MarkdownToRazor.CodeGeneration -- &quot;$(MarkdownSourceDirectory)&quot; &quot;$(GeneratedPagesDirectory)&quot;" />
</Target>

Result: Automatic /about route with your markdown content as a Blazor page!

๐Ÿ—๏ธ Architecture Overview: When to Use What

โœ… Build-Time Generation (Primary Use Case - 95% of scenarios)

What happens:

  1. Build process converts content/about.md โ†’ Pages/Generated/About.razor
  2. Generated .razor files have @page "/about" directives
  3. Blazor Router automatically discovers these routes during compilation
  4. Service registration enables dynamic navigation menus from discovered markdown files

When to use: Documentation sites, blogs, content-driven applications where routes are known at build time.

Key insight: Service registration is NOT for generating files - it's for building dynamic UI from the files that exist.

// Service registration enables dynamic navigation, NOT file generation
builder.Services.AddMarkdownToRazorServices(options =>
{
    options.SourceDirectory = "content"; // Where markdown files are
    options.BaseRoutePath = "/docs";     // Route prefix for generated pages
    // OutputDirectory NOT needed - files generated by build tools
});

Dynamic Navigation Example:

@inject IMdFileDiscoveryService DiscoveryService

<FluentNavGroup Title="Documentation">
    @foreach (var route in markdownRoutes)
    {
        <FluentNavLink Href="@route.Route">@route.Title</FluentNavLink>
    }
</FluentNavGroup>

@code {
    private MarkdownRouteInfo[] markdownRoutes = Array.Empty<MarkdownRouteInfo>();

    protected override async Task OnInitializedAsync()
    {
        markdownRoutes = await DiscoveryService.DiscoverMarkdownFilesWithRoutesAsync();
    }
}

๐Ÿ”ง Runtime Rendering (Advanced Use Case - 5% of scenarios)

What happens:

  1. No file generation - markdown rendered dynamically by MarkdownSection component
  2. Manual route handling - you create pages that use <MarkdownSection FromAsset="file.md" />
  3. Service registration provides file discovery and content loading

When to use: Dynamic content systems, CMS-like scenarios, when content changes frequently.

Important: Runtime scenarios do NOT automatically create routable pages - you must create the pages manually.

๐Ÿ“ How Markdown File Discovery Works

MarkdownToRazor follows convention-over-configuration principles to automatically discover and process your markdown files:

๐Ÿ“‚ Flexible Source Directory Configuration

The AddMarkdownToRazorServices method supports various path configurations:

// Relative paths from project root
services.AddMarkdownToRazorServices("docs/content");
services.AddMarkdownToRazorServices("../../../SharedDocumentation");

// Project root directory
services.AddMarkdownToRazorServices(".");

// Absolute paths (useful for shared content across projects)
services.AddMarkdownToRazorServices(@"C:\SharedDocs\ProjectDocs");

// Advanced configuration with recursive search
services.AddMarkdownToRazorServices(options => {
    options.SourceDirectory = "content/posts";
    options.SearchRecursively = true; // Finds files in all subdirectories
    options.FilePattern = "*.md";
});

๐ŸŽฏ Convention-Based Discovery

Default Behavior:

  • Source Directory: MDFilesToConvert/ (relative to your project root)
  • Output Directory: Pages/Generated/ (relative to your project root)
  • File Pattern: All *.md files are discovered recursively

Directory Structure Example:

YourProject/
โ”œโ”€โ”€ MDFilesToConvert/           โ† Source markdown files
โ”‚   โ”œโ”€โ”€ about.md               โ† Becomes /about route
โ”‚   โ”œโ”€โ”€ docs/
โ”‚   โ”‚   โ”œโ”€โ”€ getting-started.md โ† Becomes /docs/getting-started route
โ”‚   โ”‚   โ””โ”€โ”€ api-reference.md   โ† Becomes /docs/api-reference route
โ”‚   โ””โ”€โ”€ blog/
โ”‚       โ””โ”€โ”€ 2024/
โ”‚           โ””โ”€โ”€ news.md        โ† Becomes /blog/2024/news route
โ”œโ”€โ”€ Pages/
โ”‚   โ””โ”€โ”€ Generated/             โ† Auto-generated Razor pages
โ”‚       โ”œโ”€โ”€ About.razor        โ† Generated from about.md
โ”‚       โ”œโ”€โ”€ DocsGettingStarted.razor
โ”‚       โ”œโ”€โ”€ DocsApiReference.razor
โ”‚       โ””โ”€โ”€ Blog2024News.razor
โ””โ”€โ”€ YourProject.csproj

โš™๏ธ Configuration Options

For Build-Time Generation (Most Common):

// Program.cs - Service registration for dynamic navigation only
builder.Services.AddMarkdownToRazorServices(options =>
{
    options.SourceDirectory = "content";          // Where to find .md files
    options.BaseRoutePath = "/docs";               // Optional route prefix
    options.DefaultLayout = "MainLayout";         // Default layout component
    options.EnableYamlFrontmatter = true;         // Enable YAML frontmatter
    // OutputDirectory NOT needed - files generated by build tools
});

For Runtime Rendering (Advanced scenarios):

// Program.cs - Service registration for file loading and rendering
builder.Services.AddMarkdownToRazorServices(options =>
{
    options.SourceDirectory = "content";          // Where to find .md files
    options.FilePattern = "*.md";                 // File pattern to search for
    options.SearchRecursively = true;             // Search subdirectories
    options.EnableHtmlCommentConfiguration = true; // Enable HTML comment config
    options.EnableYamlFrontmatter = true;         // Enable YAML frontmatter
    // OutputDirectory NOT used for runtime scenarios
});

MSBuild Configuration (Build-Time Only):

<PropertyGroup>
  
  <MarkdownSourceDirectory>$(MSBuildProjectDirectory)\docs</MarkdownSourceDirectory>

  
  <GeneratedPagesDirectory>$(MSBuildProjectDirectory)\Pages\Auto</GeneratedPagesDirectory>
</PropertyGroup>

Simple Service Registration Shortcuts:

// Use defaults for navigation discovery
builder.Services.AddMarkdownToRazorServices();

// Custom source directory only
builder.Services.AddMarkdownToRazorServices("content");

๐Ÿงญ Dynamic Navigation Discovery

Use service registration to build navigation menus and UI from your markdown files:

Choose Your Service:

  • IMdFileDiscoveryService - Basic route discovery (faster, simpler)
  • IGeneratedPageDiscoveryService - Rich metadata with titles, descriptions, tags (more features)
@inject IMdFileDiscoveryService FileDiscovery

@code {
    private MarkdownRouteInfo[] navigationRoutes = Array.Empty<MarkdownRouteInfo>();

    protected override async Task OnInitializedAsync()
    {
        // Get files with their generated routes for navigation
        navigationRoutes = await FileDiscovery.DiscoverMarkdownFilesWithRoutesAsync();
    }
}

// Build navigation UI
<FluentNavGroup Title="Documentation">
    @foreach (var route in navigationRoutes)
    {
        <FluentNavLink Href="@route.Route">@route.Title</FluentNavLink>
    }
</FluentNavGroup>

Legacy File Discovery (if needed):

// Get all markdown file paths (less common)
var markdownFiles = await FileDiscovery.DiscoverMarkdownFilesAsync();
Example: Build Navigation Menu from Generated Routes

<FluentNavGroup Title="Documentation">
    @foreach (var route in documentationRoutes.Where(r => r.Route.StartsWith("/docs")))
    {
        <FluentNavLink Href="@route.Route"
                       Title="@route.Description">
            @route.Title
        </FluentNavLink>
    }
</FluentNavGroup>

<FluentNavGroup Title="Blog Posts">
    @foreach (var route in documentationRoutes.Where(r => r.Route.StartsWith("/blog")))
    {
        <FluentNavLink Href="@route.Route">@route.Title</FluentNavLink>
    }
</FluentNavGroup>

@code {
    [Inject] private IMdFileDiscoveryService FileDiscovery { get; set; } = default!;

    private MarkdownRouteInfo[] documentationRoutes = Array.Empty<MarkdownRouteInfo>();

    protected override async Task OnInitializedAsync()
    {
        documentationRoutes = await FileDiscovery.DiscoverMarkdownFilesWithRoutesAsync();
    }
}

Route Generation Examples:

  • index.md โ†’ / (root route)
  • getting-started.md โ†’ /getting-started
  • user_guide.md โ†’ /user-guide (underscores become hyphens)
  • API Reference.md โ†’ /api-reference (spaces normalized)

Available Services:

  • IMdFileDiscoveryService - Discover markdown files based on configuration
  • IStaticAssetService - Load markdown content from configured directories
  • IGeneratedPageDiscoveryService - Discover generated Razor pages with routes and metadata (new!)
  • MarkdownToRazorOptions - Access current configuration settings

๐Ÿงญ Dynamic UI Generation with Page Discovery

New in v1.3.0! The IGeneratedPageDiscoveryService allows you to build dynamic navigation and UI components by discovering all generated Razor pages with their routes and metadata.

Perfect for:

  • ๐Ÿ“‹ Dynamic navigation menus
  • ๐Ÿ” Site maps and content indexes
  • ๐Ÿ“Š Content management dashboards
  • ๐Ÿท๏ธ Tag-based content filtering
Basic Usage
@inject IGeneratedPageDiscoveryService PageDiscovery

@code {
    private List<GeneratedPageInfo> pages = new();

    protected override async Task OnInitializedAsync()
    {
        pages = (await PageDiscovery.DiscoverGeneratedPagesAsync()).ToList();
    }
}
Dynamic Navigation Component

@inject IGeneratedPageDiscoveryService PageDiscovery

<FluentNavGroup Title="Documentation">
    @foreach (var page in documentationPages.Where(p => p.Route.StartsWith("/docs") && p.ShowTitle))
    {
        <FluentNavLink Href="@page.Route"
                       Title="@page.Description">
            @page.Title
        </FluentNavLink>
    }
</FluentNavGroup>

<FluentNavGroup Title="Blog Posts">
    @foreach (var page in documentationPages.Where(p => p.Route.StartsWith("/blog")))
    {
        <FluentNavLink Href="@page.Route">@page.Title</FluentNavLink>
    }
</FluentNavGroup>

@code {
    private GeneratedPageInfo[] documentationPages = Array.Empty<GeneratedPageInfo>();

    protected override async Task OnInitializedAsync()
    {
        documentationPages = (await PageDiscovery.DiscoverGeneratedPagesAsync()).ToArray();
    }
}
Tag-Based Content Browser

@inject IGeneratedPageDiscoveryService PageDiscovery

<FluentSelect Items="@allTags" @bind-SelectedOption="@selectedTag">
    <OptionTemplate>@context</OptionTemplate>
</FluentSelect>

@if (!string.IsNullOrEmpty(selectedTag))
{
    <div class="content-grid">
        @foreach (var page in filteredPages)
        {
            <FluentCard>
                <FluentAnchor Href="@page.Route">@page.Title</FluentAnchor>
                <p>@page.Description</p>
                <div class="tags">
                    @foreach (var tag in page.Tags)
                    {
                        <FluentBadge>@tag</FluentBadge>
                    }
                </div>
            </FluentCard>
        }
    </div>
}

@code {
    private List<GeneratedPageInfo> allPages = new();
    private List<string> allTags = new();
    private string selectedTag = "";
    private List<GeneratedPageInfo> filteredPages = new();

    protected override async Task OnInitializedAsync()
    {
        allPages = (await PageDiscovery.DiscoverGeneratedPagesAsync()).ToList();
        allTags = allPages.SelectMany(p => p.Tags).Distinct().OrderBy(t => t).ToList();
    }

    private void OnTagSelected()
    {
        filteredPages = allPages.Where(p => p.Tags.Contains(selectedTag)).ToList();
    }
}
GeneratedPageInfo Properties
public class GeneratedPageInfo
{
    public string Route { get; set; }           // Page route (e.g., "/docs/getting-started")
    public string Title { get; set; }           // Page title from frontmatter or filename
    public string? Description { get; set; }    // Meta description
    public string[] Tags { get; set; }          // Tags for categorization
    public bool ShowTitle { get; set; }         // Whether to display title
    public string? Layout { get; set; }         // Layout component name
    public string FilePath { get; set; }        // Original markdown file path
}
Advanced Filtering
// Filter by tag
var docPages = await PageDiscovery.DiscoverGeneratedPagesAsync("documentation");

// Get all pages
var allPages = await PageDiscovery.DiscoverGeneratedPagesAsync();

// Filter programmatically
var blogPosts = allPages.Where(p => p.Route.StartsWith("/blog/"));
var recentPosts = allPages.Where(p => p.Tags.Contains("recent"));

2. Using MSBuild Package (Zero Configuration):

dotnet add package MarkdownToRazor --source https://nuget.pkg.github.com/DavidH102/index.json

โœจ Zero Config: The MSBuild package automatically uses conventions and runs during build!

3. Manual Tool Execution:

dotnet run --project MarkdownToRazor.CodeGeneration -- "source-dir" "output-dir"

๐Ÿ”„ Processing Behavior

What Gets Processed:

  • โœ… All .md files in source directory (recursive)
  • โœ… Files with YAML frontmatter configuration
  • โœ… Files with HTML comment configuration (new!)
  • โœ… Plain markdown files (use filename for route)

What Gets Generated:

  • ๐ŸŽฏ Razor Pages: One .razor file per markdown file
  • ๐Ÿ”— Automatic Routing: Based on file path or @page directive
  • ๐Ÿท๏ธ Page Metadata: Title, description, layout from configuration
  • ๐ŸŽจ Runtime Rendering: Uses MarkdownSection component for content

Route Generation Examples:

Source File                    โ†’  Generated Route
about.md                      โ†’  /about
docs/getting-started.md       โ†’  /docs/getting-started
blog/2024/my-post.md         โ†’  /blog/2024/my-post

With @page directive:
docs/quick-start.md           โ†’  /quick-start (if @page "/quick-start")

๐Ÿš€ Best Practices

1. Organize by Content Type:

MDFilesToConvert/
โ”œโ”€โ”€ docs/          โ† Documentation pages
โ”œโ”€โ”€ blog/          โ† Blog posts
โ”œโ”€โ”€ guides/        โ† User guides
โ””โ”€โ”€ legal/         โ† Legal pages (privacy, terms)

2. Use Meaningful File Names:

โœ… getting-started.md     โ†’ /getting-started
โœ… api-reference.md       โ†’ /api-reference
โŒ page1.md              โ†’ /page1 (not descriptive)

3. Include in Version Control:


<ItemGroup>
  <Content Include="MDFilesToConvert\**\*.md">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
  </Content>
</ItemGroup>

4. Configure Build Integration:


<Target Name="GenerateMarkdownPages" BeforeTargets="Build">
  
</Target>


<Target Name="CleanGeneratedPages" BeforeTargets="Clean">
  <RemoveDir Directories="$(GeneratedPagesDirectory)" />
</Target>

โœจ Features

  • ๐ŸŽจ Runtime Rendering: Display markdown content dynamically in your Blazor applications
  • โšก Build-Time Generation: Convert markdown files to routable Blazor pages automatically
  • ๐ŸŽฏ YAML Frontmatter & HTML Comments: Control page routing, layout, title, and metadata using either YAML frontmatter or HTML comment configuration
  • ๐Ÿ”ฅ Syntax Highlighting: Beautiful code syntax highlighting with copy-to-clipboard
  • ๐Ÿ“ฑ Responsive Design: FluentUI integration for modern, mobile-friendly layouts
  • ๐Ÿ”ง MSBuild Integration: Seamless build-time processing with zero configuration
  • ๐Ÿ“ฆ Modular Packages: Choose only the components you need

๐Ÿ’ก Use Cases

  • ๐Ÿ“š Documentation Sites: Convert markdown docs to navigable Blazor pages
  • ๐Ÿ“ Blog Platforms: Build content-driven sites with dynamic routing
  • โ“ Help Systems: Embed rich help content directly in your applications
  • ๐Ÿ”ง Content Management: Mix static markdown with dynamic Blazor components
  • ๐Ÿ“– Technical Writing: Author in markdown, publish as interactive web pages

๐Ÿ—๏ธ Architecture

Runtime Components

  • MarkdownSection.razor: Main component for dynamic markdown rendering
  • StaticAssetService: Service for loading content from files or URLs
  • Markdig Extensions: Custom extensions for enhanced code block rendering

Build-Time Tools

  • MarkdownToRazorGenerator: Core engine for converting markdown to Blazor pages
  • MSBuild Tasks: Automated integration with your build process
  • YAML Parser: Frontmatter processing for page metadata and routing

Generated Output

  • Routable Pages: Automatic Blazor page creation with proper routing
  • Layout Integration: Seamless integration with your existing Blazor layouts
  • Metadata Handling: SEO-friendly titles, descriptions, and meta tags

๐Ÿ“– Documentation

For complete guides and examples, visit our documentation:

๐Ÿค Contributing

We welcome contributions! Here's how to get involved:

  1. Fork the repository and create a feature branch
  2. Follow our coding standards and ensure tests pass
  3. Submit a pull request with a clear description of changes
  4. Join the discussion in issues and help improve the library

๐Ÿ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

๐Ÿ™ Acknowledgments

Built with these amazing open-source projects:


โญ Found this helpful? Give us a star and share with your fellow developers!

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.1.4 67 8/31/2025
2.1.3 65 8/31/2025
2.1.2 64 8/31/2025
2.1.1 104 8/31/2025
2.1.0 135 8/30/2025
2.0.2 205 8/27/2025
2.0.1 170 8/27/2025
2.0.0 179 8/27/2025