--- title: Custom Markdown Containers --- [Custom Containers](https://github.com/xoofx/markdig/blob/master/src/Markdig.Tests/Specs/CustomContainerSpecs.md) are a popular method for implementing Markdown Extensions for enabling rich, wrist-friendly consistent content in your Markdown documents. ## Built-in Containers Most of [VitePress Containers](https://vitepress.dev/guide/markdown#custom-containers) are also implemented in Razor Press, e.g: #### Input ```markdown ::: info This is an info box. ::: ::: tip This is a tip. ::: ::: warning This is a warning. ::: ::: danger This is a dangerous warning. ::: ``` #### Output ::: info This is an info box. ::: ::: tip This is a tip. ::: ::: warning This is a warning. ::: ::: danger This is a dangerous warning. ::: ### Custom Title You can specify a custom title by appending the text right after the container type: #### Input ```markdown ::: danger STOP Danger zone, do not proceed ::: ``` #### Output ::: danger STOP Danger zone, do not proceed ::: ### Pre The **pre** container can be used to capture its content in a `
` element instead of it's default markdown rendering: ```markdown :::pre ... ::: ``` ### copy The **copy** container is ideal for displaying text snippets in a component that allows for easy copying: #### Input ```markdown :::copy Copy Me! ::: ``` #### Output :::copy Copy Me! ::: HTML or XML fragments can also be copied by escaping them first: #### Input ```markdown :::copy `` ::: ``` #### Output :::copy ` ` ::: ### sh Similarly the **sh** container is ideal for displaying and copying shell commands: #### Input ```markdown :::sh npm run prerender ::: ``` #### Output :::sh npm run prerender ::: ## Implementing Block Containers [Markdig Containers](https://github.com/xoofx/markdig/blob/master/src/Markdig.Tests/Specs/CustomContainerSpecs.md) are a great way to create rich widgets that can be used directly in Markdown. They're useful for ensuring similar content is displayed consistently across all your documentation. A good use-case for this could be to implement a YouTube component for standardizing how YouTube videos are displayed. For this example we want to display a YouTube video using just its YouTube **id** and a **title** for the video which we can capture in the Custom Container: ```markdown :::YouTube MRQMBrXi5Sc Using Razor SSG to Create Websites in GitHub Codespaces ::: ``` Which we can implement with a normal Markdig `HtmlObjectRenderer `: ```csharp public class YouTubeContainer : HtmlObjectRenderer { protected override void Write(HtmlRenderer renderer, CustomContainer obj) { if (obj.Arguments == null) { renderer.WriteLine($"Missing YouTube Id, Usage :::{obj.Info} "); return; } renderer.EnsureLine(); var youtubeId = obj.Arguments!; var attrs = obj.TryGetAttributes()!; attrs.Classes ??= new(); attrs.Classes.Add("not-prose text-center"); renderer.Write(" '); renderer.WriteLine(""); } } ``` That should be registered in `Configure.Ssg.cs` with the name we want to use for the container: ```csharp MarkdigConfig.Set(new MarkdigConfig { ConfigureContainers = config => { // Add Custom Block or Inline containers config.AddBlockContainer("YouTube", new YouTubeContainer()); } }); ``` After which it can be used in your Markdown documentation: #### Input ```markdown :::YouTube MRQMBrXi5Sc Using Razor SSG to Create Websites in GitHub Codespaces ::: ``` #### Output :::YouTube MRQMBrXi5Sc Using Razor SSG to Create Websites in GitHub Codespaces ::: ### Custom Attributes Since we use `WriteAttributes(obj)` to emit any attributes we're also able to customize the widget to use a custom **id** and classes, e.g: #### Input ```markdown :::YouTube MRQMBrXi5Sc {.text-indigo-600} Using Razor SSG to Create Websites in GitHub Codespaces ::: ``` #### Output :::YouTube MRQMBrXi5Sc {.text-indigo-600} Using Razor SSG to Create Websites in GitHub Codespaces ::: ## Implementing Inline Containers Custom Inline Containers are useful when you don't need a to capture a block of content, like if we just want to display a video without a title, e.g: ```markdown ::YouTube MRQMBrXi5Sc:: ``` Inline Containers can be implemented with a Markdig `HtmlObjectRenderer"); renderer.WriteChildren(obj); renderer.WriteLine(""); renderer.WriteLine(@$"`, e.g: ```csharp public class YouTubeInlineContainer : HtmlObjectRenderer { protected override void Write(HtmlRenderer renderer, CustomContainerInline obj) { var youtubeId = obj.FirstChild is Markdig.Syntax.Inlines.LiteralInline literalInline ? literalInline.Content.AsSpan().RightPart(' ').ToString() : null; if (string.IsNullOrEmpty(youtubeId)) { renderer.WriteLine($"Missing YouTube Id, Usage ::YouTube ::"); return; } renderer.WriteLine(@$" "); } } ``` That can be registered in `Configure.Ssg.cs` with: ```csharp MarkdigConfig.Set(new MarkdigConfig { ConfigureContainers = config => { // Add Custom Block or Inline containers config.AddInlineContainer("YouTube", new YouTubeInlineContainer()); } }); ``` Where it can then be used in your Markdown documentation: #### Input ```markdown ::YouTube MRQMBrXi5Sc:: ``` #### Output ::YouTube MRQMBrXi5Sc::