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
- .NET 11 SDK installed
- Completed Scaffold a blog with BlogSite
- Completed Author your first post with BlogSiteFrontMatter
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
Add
HeroContentto the optionsOpen the
AddBlogSitecall from the previous tutorial and add one property.HeroContentis a two-field positional record —TitleandDescription— 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 runand visithttp://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
Build the project array
Projectis a three-field positional record —Title,Description,Url— populated with a C# collection expression right belowHeroContent. TheUrlbecomes 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
MyWorkproperty is typed asIReadOnlyList<Project>onBlogSiteOptions. 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 runand visithttp://localhost:5000/ - A "My Work" card appears in the home-page right rail with three linked entries — Pennington, MonorailCSS, Mdazor — each clickable
3. Wire social links with the built-in icons
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
Add a
usingforSocialIconsand fourSocialLinksThis step adds two things: a
using Pennington.BlogSite.Components;directive at the top ofProgram.cssoSocialIcons.GithubIconresolves, and aSocials = [...]block with four entries covering all four built-ins (GithubIcon,BlueskyIcon,LinkedInIcon,MastodonIcon). Each field is aRenderFragmentvalue — pass the field itself, nottypeof(...)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")passesSocialIcons.GithubIcon— theRenderFragmentdelegate 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 runand visithttp://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
4. Add header links for top-nav
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
Confirm the three header links resolve
Look at the
MainSiteLinks = [...]block pasted in Step 3.1. It contains three entries —Homepointing to/,Archiveto/archive, andTagsto/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.
Checkpoint — The top-nav and footer populate
- Run
dotnet runand visithttp://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
HeroContentnow drives the home-page headline block.- A
Project[]onMyWorkbrought the "My Work" sidebar card to life with three linked entries. - Four
SocialLinkentries wire the built-inSocialIcons.GithubIcon,BlueskyIcon,LinkedInIcon, andMastodonIconRenderFragmentfields — no Razor required. MainSiteLinksholds threeHeaderLinkentries, 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.