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

Skip to main content Skip to navigation

Wire the blog homepage hero

When a BlogSite homepage needs its hero block, "My Work" card, social-icon row, and top-nav links populated in one pass, the four init-only properties on BlogSiteOptions cover it. For the hand-held walkthrough, see Add a hero, projects, and social links.

Assumptions

For a working setup, see examples/BlogSiteHeroProjectsSocialsExample. This page is a recipe, not a tour, so it does not walk through the whole example.


Options

Set HeroContent for the headline block

HeroContent is a two-field positional record (Title, Description) rendered at the top of /. Description is emitted as a MarkupString in Home.razor, so light HTML is permitted; plain prose works for most sites.

/// <summary>Hero block at the top of the blog homepage.</summary>
/// <param name="Title">Hero headline.</param>
/// <param name="Description">Hero subhead/body text.</param>
public record HeroContent(string Title, string Description);
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();

Fill MyWork with Project entries

MyWork takes a Project[], where each Project(Title, Description, Url) renders as a linked entry in the "My Work" sidebar card. The array is rendered verbatim, so ordering entries in the initializer controls their display order.

/// <summary>Featured project card shown on the blog homepage.</summary>
/// <param name="Title">Project title.</param>
/// <param name="Description">Short project description.</param>
/// <param name="Url">Link target for the card.</param>
public record Project(string Title, string Description, string 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();

Wire Socials with the built-in icon fragments

Socials takes a SocialLink[], where SocialLink(Icon, Url) pairs a RenderFragment with an <a href> target. The four built-in fragments — GithubIcon, BlueskyIcon, LinkedInIcon, MastodonIcon — are static readonly fields on Pennington.BlogSite.Components.SocialIcons and are passed directly without any wrapper type or component registration.

/// <summary>Icon and URL for a social media link.</summary>
/// <param name="Icon">Rendered icon markup.</param>
/// <param name="Url">Target URL.</param>
public record SocialLink(RenderFragment Icon, string Url);
@code {
    public static readonly RenderFragment GithubIcon = @<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none">
        <path d="M6.51734 17.1132C6.91177 17.6905 8.10883 18.9228 9.74168 19.2333M9.86428 22C8.83582 21.8306 2 19.6057 2 12.0926C2 5.06329 8.0019 2 12.0008 2C15.9996 2 22 5.06329 22 12.0926C22 19.6057 15.1642 21.8306 14.1357 22C14.1357 22 13.9267 18.5826 14.0487 17.9969C14.1706 17.4113 13.7552 16.4688 13.7552 16.4688C14.7262 16.1055 16.2043 15.5847 16.7001 14.1874C17.0848 13.1032 17.3268 11.5288 16.2508 10.0489C16.2508 10.0489 16.5318 7.65809 15.9996 7.56548C15.4675 7.47287 13.8998 8.51192 13.8998 8.51192C13.4432 8.38248 12.4243 8.13476 12.0018 8.17939C11.5792 8.13476 10.5568 8.38248 10.1002 8.51192C10.1002 8.51192 8.53249 7.47287 8.00036 7.56548C7.46823 7.65809 7.74917 10.0489 7.74917 10.0489C6.67316 11.5288 6.91516 13.1032 7.2999 14.1874C7.79575 15.5847 9.27384 16.1055 10.2448 16.4688C10.2448 16.4688 9.82944 17.4113 9.95135 17.9969C10.0733 18.5826 9.86428 22 9.86428 22Z" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
    </svg>;
  
    public static readonly RenderFragment LinkedInIcon = @<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none">
        <path d="M7 10V17" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
        <path d="M11 13V17M11 13C11 11.3431 12.3431 10 14 10C15.6569 10 17 11.3431 17 13V17M11 13V10" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
        <path d="M7.00801 7L6.99902 7" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
        <path d="M2.5 12C2.5 7.52166 2.5 5.28249 3.89124 3.89124C5.28249 2.5 7.52166 2.5 12 2.5C16.4783 2.5 18.7175 2.5 20.1088 3.89124C21.5 5.28249 21.5 7.52166 21.5 12C21.5 16.4783 21.5 18.7175 20.1088 20.1088C18.7175 21.5 16.4783 21.5 12 21.5C7.52166 21.5 5.28249 21.5 3.89124 20.1088C2.5 18.7175 2.5 16.4783 2.5 12Z" stroke-width="1.5" stroke-linejoin="round"></path>
    </svg>;
  
    public static readonly RenderFragment BlueskyIcon = @<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none">
        <path d="M12 11.4963C11.8936 11.2963 7.45492 3 3.50417 3C1.33647 3 2.00456 8 2.50443 10.5C2.70653 11.5108 3.50417 14.5 8.003 14C8.003 14 4.00404 14.5 4.00404 17C4.00404 18.5 6.50339 21 8.50287 21C10.4606 21 11.9391 16.6859 12 16.5058C12.0609 16.6859 13.5394 21 15.4971 21C17.4966 21 19.996 18.5 19.996 17C19.996 14.5 15.997 14 15.997 14C20.4958 14.5 21.2935 11.5108 21.4956 10.5C21.9954 8 22.6635 3 20.4958 3C16.5451 3 12.1064 11.2963 12 11.4963Z" stroke-width="1.5" stroke-linejoin="round"></path>
    </svg>;
  
    public static readonly RenderFragment MastodonIcon = @<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none">
        <path d="M17 13.5V8C17 6.61929 15.8807 5.5 14.5 5.5C13.1193 5.5 12 6.61929 12 8M12 8V11.5M12 8C12 6.61929 10.8807 5.5 9.5 5.5C8.11929 5.5 7 6.61929 7 8V13.5" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
        <path d="M13 16.9954C15.0099 16.9954 16.89 16.6876 18.4949 16.1525C20.1275 15.6081 21 13.9512 21 12.2302V7.52349C21 5.34784 19.8297 3.2779 17.7281 2.715C16.0259 2.25905 14.0744 2 12 2C9.9256 2 7.97414 2.25905 6.27189 2.715C4.17033 3.2779 3 5.34785 3 7.52349V14.4961C3 22.4937 11 21.9938 11 21.9938C13.5 21.9938 15 21 15 21V20C15 20 13.5 20.4943 11 20.4943C5.68009 20.4943 7.06011 15.9957 7.06011 15.9957C8.75781 16.627 10.8012 16.9954 13 16.9954Z" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
    </svg>;
}

MainSiteLinks takes a HeaderLink[], where each HeaderLink(Title, Url) appears in both the site header and footer via MainLayout.razor. Use relative URLs (/, /archive, /tags) so BaseUrlHtmlRewriter can prefix them correctly on sub-path deployments.

/// <summary>Link rendered in the main site navigation.</summary>
/// <param name="Title">Display text.</param>
/// <param name="Url">Target URL.</param>
public record HeaderLink(string Title, string 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"),
    ],
  
    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();

Result

The homepage at / renders the hero block above the post list, the "My Work" card and social-icon row in the right rail, and every HeaderLink in both the top nav and the footer. The four surfaces are independent; populating any one renders that surface and leaves the rest at their template defaults.

Verify

  • Run dotnet run and open /. The hero title and description appear at the top, and the "My Work" card lists each Project entry with a working link.
  • The social-icon row under the card renders one icon per SocialLink, each linking to its Url. The top-nav and footer list every HeaderLink.
  • Run dotnet run -- build. The generated index.html contains every hero/project/socials/nav string, and the build report shows no 500s.