Using Nitride - Front Matter

Being able to associated metadata for a page is a very useful thing. It can be used to group pages into categories, control how it is styled, or to simply provide internal notes. To do that, we use something called a YAML front matter to describe the details.

---
title: Name of the Page
summary: Summary of page
image: /path/to/image.png
---

This is the front page.

This is a major functionality of the Markdown + YAML pages that I use in my technical, fantasy, and even blog posts. I use the categories and tags heavily on this page, not to mention giving a summary details for links. It can also be used to provide OpenGraph schemas.

Series

  • 2025-06-07 Using Nitride - Introduction - I'm going to start a long, erratic journey to update my publisher website. Along the way, I'm going to document how to create a MfGames.Nitride website from the ground up, along with a lot of little asides about reasoning or purpose.

  • 2025-06-08 Using Nitride - Pipelines - The first major concept of Nitride is the idea of a "pipeline" which are the largest units of working in the system. This shows a relatively complex setup of pipelines that will be used to generate the website.

  • 2025-06-09 Using Nitride - Entities - The continued adventures of creating the Typewriter website using MfGames.Nitride, a C# static site generator. The topic of today is the Entity class and how Nitride uses an ECS (Entity-Component-System) to generate pages.

  • 2025-06-10 Using Nitride - Markdown - Examples and explanations of converting Markdown to HTML using MfGames.Nitride and MarkDig.

  • 2025-06-12 Using Nitride - Front Matter - How to use Nitride to take the front matter from pages and add them as a component into the Entity class.

Defining a Model

Because C# is a static language, we want to take advantage of a schema so we can have type-safe. Historically, I've called this PageModel because I wrap that in a TemplateModel when I get to the styling.

// In //src/dotnet/Models/PageModel.cs
namespace Generator.Models;

public class PageModel
{
    /// <summary>
    ///     Gets or sets the optional list of categories associated with the page.
    /// </summary>
    public List<string>? Categories { get; set; }

    /// <summary>
    ///     Gets or sets the URL of the image associated with the page.
    /// </summary>
    public string? Image { get; set; }

    /// <summary>
    ///     Gets or sets the summary for the page.
    /// </summary>
    public string? Summary { get; set; }

    /// <summary>
    ///     Gets or sets the title of the page.
    /// </summary>
    public string? Title { get; set; }
}

Naturally, this can and will get a lot more complicated. Because we are using YAML, we can have nested objects, tags, and references that are appropriate for our page. In the above example, we are defining an optional title, summary, an image, and a list of categories.

It may come to a surprise to you, but this will eventually become a component in the page Entity class that is passed through the pipelines.

Including the YAML module.

Like the others MfGames.Nitride modules, we have to add it as a NuGet reference and tell the system to use it.

cd src/dotnet
dotnet add package MfGames.Nitride.Yaml

Adding it to the system is just a matter of modifying Program.cs to include it.

// In //src/dotnet/Program.cs
var builder = new NitrideBuilder(args)
    .UseIO(rootDirectory)
    .UseMarkdown()
    .UseHtml()
    .UseYaml()
    .UseModule<WebsiteModule>();

Parsing Front Matter

Parsing the front matter uses an operation, MfGames.Nitride.Yaml.ParseYamlHeader to parse the text content, pull off the front matter, wrap it in the model call, and then replace ITextContent with the page without the YAML header.

In effect, this:

---
title: Name of the Page
summary: Summary of page
image: /path/to/image.png
---

This is the front page.

... becomes “This is the front page.” in the text content with a PageModel component.

Adding the operation is relatively simple but this operation uses a generic parameter to identify the model to parse the YAML as.

// In //src/dotnet/Pipelines/Inputs/PagesModel.cs
public PagesPipeline(
    ILogger<PagesPipeline> logger,
    ReadFiles readFiles,
    IdentifyMarkdownFromPath identifyMarkdownFromPath,
    MoveToIndexPath moveToIndexPath,
    ParseYamlHeader<PageModel> parseYamlHeader)
{
    _logger = logger;
    _identifyMarkdownFromPath = identifyMarkdownFromPath;
    _moveToIndexPath = moveToIndexPath;
    _parseYamlHeader = parseYamlHeader;

And, like the others, it's just a matter of adding the operation into the pipeline.

// In //src/dotnet/Pipelines/Inputs/PagesModel.cs
var list = _readFiles
    .Run(cancellationToken)
    .Run(_identifyMarkdownFromPath)
    .Run(_parseYamlHeader)
    .Run(_moveToIndexPath)
    .ToList();

Once we run it, we get the following output:

[01:44:05 INF] <PagesPipeline> Entity: Path /contact/index.md, Components ["Zio.UPath","Generator.Models.PageModel","MfGames.Nitride.Contents.ITextContent","MfGames.Nitride.Markdown.IsMarkdown"]
[01:44:05 INF] <PagesPipeline> Entity: Path /index.md, Components ["Zio.UPath","Generator.Models.PageModel","MfGames.Nitride.Contents.ITextContent","MfGames.Nitride.Markdown.IsMarkdown"]

When we look at the HTML output, you'll notice it has a simplified version.

$ cat build/typewriter/html/index.html 
<h1>Typewriter Press</h1>

Retrieving the component is code just requires the Get<PageModel>() method or to use the various LINQ-based calls from MfGames.Gallium.

var page = entity.Get<PageModel>();

var list = entities
    .SelectEntity<PageModel>(OnlyRunOnEntitiesWithPageModel)
    .ToList();

public Entity OnlyRunOnEntitiesWithPageModel(Entity entity, PageModel page)
{
    return entity.Set(somePageSpecificVariable);
}

Gallium Methods

In the above case, the SelectEntity call will skip calling the lambda for entities that don't have a PageModel, but will still return it into the list. This is the “systems” part of the ECS system. The following will strip out the entities that don't have the appropriate models.

.SelectEntity<PageModel>((entity, page) => entity, includeEntitiesWithoutComponents: false)

So far, Gallium is set up to allow up to four components. I haven't had a need to do more, but those are easy to add. This makes it useful when doing some model processing on files that have a future date.

.WhereEntity<PageModel, UPage, Instant>(FilterOutFuturePages)

The SelectEntity is rather powerful because it takes an Entity and returns one. It can be the same entity, or it can be one that has zero or more components added or changed inside it. I found this really useful when I'm parsing out categories or I'm trying to build up a list of some specialized functionality that the styling will need.

public Entity OnSelectEntity(
    Entity entity,
    PageModel page,
    UPath path,
    ITextContent textContent)
{
    return entity
        .Set(nextPreviousModel)
        .SetAll(parentPage, parentUrl)
        .Set(modifiedPageModel);
}

What's Next

We have the minimum number of components and systems needed to setup styling. Next time, I'll use Handlebars to style the page and put a little chrome around the output.

  • 2025-06-07 Using Nitride - Introduction - I'm going to start a long, erratic journey to update my publisher website. Along the way, I'm going to document how to create a MfGames.Nitride website from the ground up, along with a lot of little asides about reasoning or purpose.

  • 2025-06-08 Using Nitride - Pipelines - The first major concept of Nitride is the idea of a "pipeline" which are the largest units of working in the system. This shows a relatively complex setup of pipelines that will be used to generate the website.

  • 2025-06-09 Using Nitride - Entities - The continued adventures of creating the Typewriter website using MfGames.Nitride, a C# static site generator. The topic of today is the Entity class and how Nitride uses an ECS (Entity-Component-System) to generate pages.

  • 2025-06-10 Using Nitride - Markdown - Examples and explanations of converting Markdown to HTML using MfGames.Nitride and MarkDig.

  • 2025-06-12 Using Nitride - Front Matter - How to use Nitride to take the front matter from pages and add them as a component into the Entity class.

Metadata

Categories:

Tags: