This site provides a machine-readable index at /llms.txt.

Skip to main content Skip to navigation

Add a hero, projects, and social links

By the end of this tutorial, the BlogSite host displays a hero headline on the home page, a "My Work" sidebar card listing three projects, a row of four social-media icons beneath it, and a top-nav bar populated from MainSiteLinks. Along the way, HeroContent, Project, SocialLink, and HeaderLink on BlogSiteOptions come into play, plus the four built-in icon RenderFragment fields from SocialIcons — all without a line of Razor.

Prerequisites

The finished code for this tutorial lives in examples/BlogSiteHeroProjectsSocialsExample.


1. Populate the hero block

The BlogSite home page renders a headline block at the very top, driven entirely by BlogSiteOptions.HeroContent. Nothing outside the options call needs to change.

  1. 1

    Add HeroContent to the options

    Open the AddBlogSite call from the previous tutorial and add one property. HeroContent is a two-field positional record — Title and Description — so a single constructor call is all it takes.

    var builder = WebApplication.CreateBuilder(args);
      
    builder.Services.AddBlogSite(() => new BlogSiteOptions
    {
        SiteTitle = "Hero Blog",
        Description = "A BlogSite tutorial app demonstrating hero, projects, and social links.",
        CanonicalBaseUrl = "https://example.com",
      
        AuthorName = "Author Name",
        AuthorBio = "Writing about software, tools, and the occasional side project.",
      
        HeroContent = new HeroContent(
            Title: "Field notes from a weekend content engine",
            Description: "I build small tools for small problems. This is where I write about them."),
    });
      
    var app = builder.Build();
    app.UseBlogSite();
    app.RunBlogSiteAsync(args).GetAwaiter().GetResult();

    The HeroContent = new HeroContent(Title: …, Description: …) assignment is the only addition — no new DI registrations, no new Razor files, no front matter changes. The rest of the options block carries forward unchanged from the scaffold tutorial.

Checkpoint — The hero renders

  • Run dotnet run and visit http://localhost:5000/
  • The hero title "Field notes from a weekend content engine" and the description paragraph stack above the recent-posts list from the first-post tutorial

2. Add a "My Work" projects section

BlogSiteOptions.MyWork accepts a Project[] that the home page renders as a sidebar card titled "My Work". Each entry becomes an anchor wrapping a title-and-description pair.

  1. 1

    Build the project array

    Project is a three-field positional record — Title, Description, Url — populated with a C# collection expression right below HeroContent. The Url becomes the <a href> around each rendered entry, so it can point at a GitHub repo, a product page, or any other URL.

    var builder = WebApplication.CreateBuilder(args);
      
    builder.Services.AddBlogSite(() => new BlogSiteOptions
    {
        SiteTitle = "Hero Blog",
        Description = "A BlogSite tutorial app demonstrating hero, projects, and social links.",
        CanonicalBaseUrl = "https://example.com",
      
        AuthorName = "Author Name",
        AuthorBio = "Writing about software, tools, and the occasional side project.",
      
        HeroContent = new HeroContent(
            Title: "Field notes from a weekend content engine",
            Description: "I build small tools for small problems. This is where I write about them."),
      
        MyWork =
        [
            new Project(
                Title: "Pennington",
                Description: "A tiny .NET content engine for docs and blogs.",
                Url: "https://github.com/example/pennington"),
            new Project(
                Title: "MonorailCSS",
                Description: "Utility-first CSS generation for Razor.",
                Url: "https://github.com/example/monorailcss"),
            new Project(
                Title: "Mdazor",
                Description: "Inline Razor components inside Markdown.",
                Url: "https://github.com/example/mdazor"),
        ],
    });
      
    var app = builder.Build();
    app.UseBlogSite();
    app.RunBlogSiteAsync(args).GetAwaiter().GetResult();

    The MyWork property is typed as IReadOnlyList<Project> on BlogSiteOptions. Its default is an empty list, so the "My Work" card stays invisible in the UI until populated here.

Checkpoint — The sidebar card appears

  • Run dotnet run and visit http://localhost:5000/
  • A "My Work" card appears in the home-page right rail with three linked entries — Pennington, MonorailCSS, Mdazor — each clickable

Social links are SocialLink(RenderFragment Icon, string Url) records. The four built-in icons ship as static readonly RenderFragment fields on Pennington.BlogSite.Components.SocialIcons, referenced directly — no component instantiation needed.

  1. 1

    Add a using for SocialIcons and four SocialLinks

    This step adds two things: a using Pennington.BlogSite.Components; directive at the top of Program.cs so SocialIcons.GithubIcon resolves, and a Socials = [...] block with four entries covering all four built-ins (GithubIcon, BlueskyIcon, LinkedInIcon, MastodonIcon). Each field is a RenderFragment value — pass the field itself, not typeof(...) and not <GithubIcon />.

    var builder = WebApplication.CreateBuilder(args);
      
    builder.Services.AddBlogSite(() => new BlogSiteOptions
    {
        SiteTitle = "Hero Blog",
        Description = "A BlogSite tutorial app demonstrating hero, projects, and social links.",
        CanonicalBaseUrl = "https://example.com",
      
        AuthorName = "Author Name",
        AuthorBio = "Writing about software, tools, and the occasional side project.",
      
        HeroContent = new HeroContent(
            Title: "Field notes from a weekend content engine",
            Description: "I build small tools for small problems. This is where I write about them."),
      
        MyWork =
        [
            new Project(
                Title: "Pennington",
                Description: "A tiny .NET content engine for docs and blogs.",
                Url: "https://github.com/example/pennington"),
            new Project(
                Title: "MonorailCSS",
                Description: "Utility-first CSS generation for Razor.",
                Url: "https://github.com/example/monorailcss"),
            new Project(
                Title: "Mdazor",
                Description: "Inline Razor components inside Markdown.",
                Url: "https://github.com/example/mdazor"),
        ],
      
        Socials =
        [
            new SocialLink(SocialIcons.GithubIcon, "https://github.com/example"),
            new SocialLink(SocialIcons.BlueskyIcon, "https://bsky.app/profile/example.bsky.social"),
            new SocialLink(SocialIcons.LinkedInIcon, "https://www.linkedin.com/in/example"),
            new SocialLink(SocialIcons.MastodonIcon, "https://hachyderm.io/@example"),
        ],
      
        MainSiteLinks =
        [
            new HeaderLink("Home", "/"),
            new HeaderLink("Archive", "/archive"),
            new HeaderLink("Tags", "/tags"),
        ],
    });
      
    var app = builder.Build();
    app.UseBlogSite();
    app.RunBlogSiteAsync(args).GetAwaiter().GetResult();

    Notice that new SocialLink(SocialIcons.GithubIcon, "https://github.com/example") passes SocialIcons.GithubIcon — the RenderFragment delegate itself — as the first positional argument. BlogSite invokes that delegate inside the <a href> at render time. For custom SVG icons, see the Extensibility how-tos.

Checkpoint — The icon row renders under "My Work"

  • Run dotnet run and visit http://localhost:5000/
  • A horizontal row of four SVG icons — GitHub, Bluesky, LinkedIn, Mastodon — sits below the "My Work" card, each linking out to its Url

The same Stage3.Run listing from earlier includes the final surface: MainSiteLinks, a HeaderLink[] that BlogSite renders in both the top-nav of MainLayout.razor and the footer. Each entry is a HeaderLink(string Title, string Url) positional record.

  1. 1

    Confirm the three header links resolve

    Look at the MainSiteLinks = [...] block pasted in Step 3.1. It contains three entries — Home pointing to /, Archive to /archive, and Tags to /tags. No additional code is needed here; this step exists to verify that the nav URLs line up with the routes BlogSite exposes out of the box.

  • Run dotnet run and visit http://localhost:5000/
  • A "Home / Archive / Tags" link row appears in the site header, and the same three links repeat in the footer nav
  • Click Archive — the archive page lists the first post from the previous tutorial

Summary

  • HeroContent now drives the home-page headline block.
  • A Project[] on MyWork brought the "My Work" sidebar card to life with three linked entries.
  • Four SocialLink entries wire the built-in SocialIcons.GithubIcon, BlueskyIcon, LinkedInIcon, and MastodonIcon RenderFragment fields — no Razor required.
  • MainSiteLinks holds three HeaderLink entries, and they render in both the top-nav and the footer.
  • The four homepage surfaces on BlogSiteOptions — hero, work, socials, header links — are in hand, along with the record type that drives each one.