Yesterday, I published an article introducing this library: Displaying Markdown Files in C# Blazor, which explains how to display Markdown content in Blazor. The code in that article had no syntax highlighting. After much deliberation, I decided to improve it, so I searched online and found this article: .NET C# Blazor Server-Side Rendering Markdown. The current rendering effect is as follows:

I think it's quite satisfactory now. Let me explain how it's done.
1. Preparation
1.1 Add Markdown to HTML package: Markdig
Markdig: Markdig is a fast, powerful, CommonMark compliant, extensible .NET Markdown processor.
<PackageReference Include="Markdig" Version="0.27.0" />
1.2 Introduce Prism Plugin
This Prism is not the other Prism. It's a JS plugin: Prism is a lightweight, robust, and elegant syntax highlighting library. It's a derivative project of Dabblet.
Include it in the head of _Layout.cshtml:
<head>
....
<!--Reset browser styles-->
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/normalize.css@8.0.1/normalize.css"
/>
<!--Code block theme-->
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/prismjs@1.27.0/themes/prism-coy.min.css"
/>
<!--Toolbar plugin-->
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/prismjs@1.27.0/plugins/toolbar/prism-toolbar.min.css"
/>
<!--Line numbers plugin-->
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/prismjs@1.27.0/plugins/line-numbers/prism-line-numbers.min.css"
/>
...
</head>
<body>
...
<!--Prism core JS (for rendering code blocks)-->
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.27.0/prism.min.js"></script>
<!--Display code block line numbers-->
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.27.0/plugins/line-numbers/prism-line-numbers.min.js"></script>
<!--Toolbar (prerequisite for some plugins)-->
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.27.0/plugins/toolbar/prism-toolbar.min.js"></script>
<!--Display language name on code blocks-->
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.27.0/plugins/show-language/prism-show-language.min.js"></script>
<!--Copy code-->
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.27.0/plugins/copy-to-clipboard/prism-copy-to-clipboard.min.js"></script>
<!--Automatically load language-specific syntax highlighting JS from CDN-->
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.27.0/plugins/autoloader/prism-autoloader.min.js"></script>
</body>
2. Usage
I extracted the Markdown display into a separate component MarkdownComponent.razor, using the relative path of the loaded Markdown file, the article link, and the source code link as parameters for reuse in other tools. The code snippets below are mainly from this file.
Component parameter definition:
@code {
[Parameter]
public string LocalPostFilePath { get; set; } = null!;
[Parameter]
public string RemotePostUrl { get; set; } = null!;
[Parameter]
public string SourceCodeUrl { get; set; } = null!;
}
Markdown content reading and conversion from Markdown format to HTML are defined in the OnInitializedAsync() method:
protected override async Task OnInitializedAsync()
{
var markdownData = await File.ReadAllTextAsync(LocalPostFilePath);
// Convert markdown to html
var htmlData = Markdown.ToHtml(markdownData);
// Convert to Prism-supported language tags (optional, can be removed)
htmlData = htmlData.Replace("language-golang", "language-go");
// TODO: Use https://github.com/mganss/HtmlSanitizer to sanitize XSS in HTML
if (htmlData.Contains("<script") || htmlData.Contains("<link"))
{
_hasXss = true;
}
// Convert plain text to renderable HTML type
_postHtmlContent = (MarkupString) htmlData;
}
The final step is to call the Prism plugin method after the component is rendered, written in the method OnAfterRenderAsync(bool firstRender). This is the key code for syntax highlighting:
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await _jsRuntime.InvokeVoidAsync("Prism.highlightAll");
}
Rendering is relatively simple (for our usage). See the code below:
<div class="line-numbers">
@{ if (_hasXss) { @_postHtmlContent.ToString() } else { @_postHtmlContent } }
</div>
Call the component in IcoTool.razor:
<MarkdownComponent
LocalPostFilePath="wwwroot/2022/02/2022-02-22_02.md"
RemotePostUrl="https://dotnet9.com/2022/02/Perfect-Display-Markdown-in-Csharp-Blazor-and-add-code-highlighting"
SourceCodeUrl="https://github.com/dotnet9/dotnet9.com/blob/develop/src/Dotnet9.Tools.Web/Pages/Public/ImageTools/IcoTool.razor"
/>
Of course, component encapsulation depends on individual needs. The above gives the general idea. I won't post the detailed code. If you're interested, check out the Dotnet9 Toolbox source code.
Reference article: