---
title: "Add a hero, projects, and social links"
description: "Populate the four BlogSite homepage surfaces — hero block, My Work card, social-icon row, and top-nav links — on BlogSiteOptions."
canonical_url: https://usepennington.net/tutorials/blogsite/hero-projects-socials/
sidecar_url: https://usepennington.net/tutorials/blogsite/hero-projects-socials.md
content_hash: sha256:8d61ce61a9a6106d119fd0a22c5fdcb24621480effa43c9e6cf96c1a5daf545a
tokens: 2420
uid: tutorials.blogsite.hero-projects-socials
reading_time_minutes: 4
---

Getting Started
# Add a hero, projects, and social links

Populate the four BlogSite homepage surfaces — hero block, My Work card, social-icon row, and top-nav links — on BlogSiteOptions.

 
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` — all driven by `BlogSiteOptions` without a line of Razor.

 
## Prerequisites

 
 - .NET 10 SDK installed
 - Completed [Scaffold a blog with BlogSite](https://usepennington.net/tutorials/blogsite/scaffold.md)
 - Completed [Publish your first post and light up the RSS feed](https://usepennington.net/tutorials/blogsite/first-post.md)
 
 
The finished code for this tutorial lives in [examples/BlogSiteHeroProjectsSocialsExample](https://github.com/usepennington/pennington/tree/main/examples/BlogSiteHeroProjectsSocialsExample).

 
---

 
## 1. Populate the hero block

 
The BlogSite home page renders a headline block at the very top, driven entirely by `BlogSiteOptions.HeroContent`. `HeroContent` is a two-field positional record — `Title` and `Description` — so a single constructor call is all it takes.

 
**Add `HeroContent` to the `AddBlogSite` call**

 
Open the `AddBlogSite` call from the previous tutorial and add one property. 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.

 
```csharp:symbol,bodyonly
var builder = WebApplication.CreateBuilder(args);
  
builder.Services.AddBlogSite(() => new BlogSiteOptions
{
    SiteTitle = "Hero Blog",
    SiteDescription = "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();
```

 
> [!CHECKPOINT]
>  - 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". `Project` is a three-field positional record — `Title`, `Description`, `Url`. 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.

 
**Populate `MyWork` with a `Project[]` collection expression**

 
Add the `MyWork` property right below `HeroContent`, populated with a C# collection expression. The property is typed as `Project[]` on `BlogSiteOptions`; its default is an empty list, so the "My Work" card stays invisible in the UI until populated here.

 
```csharp:symbol,bodyonly
var builder = WebApplication.CreateBuilder(args);
  
builder.Services.AddBlogSite(() => new BlogSiteOptions
{
    SiteTitle = "Hero Blog",
    SiteDescription = "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();
```

 
> [!CHECKPOINT]
>  - Run `dotnet run` and visit `http://localhost:5000/`
>  - A "My Work" sidebar card appears with three linked entries — Pennington, MonorailCSS, Mdazor — each clickable

---

 
## 3. Wire social links and top-nav header links

 
The final two surfaces land in the same `AddBlogSite` call. `Socials` is a `SocialLink(RenderFragment Icon, string Url)[]`; `MainSiteLinks` is a `HeaderLink(string Title, string Url)[]` that BlogSite renders in both the top-nav of `MainLayout.razor` and the footer. The listing below adds both.

 
**Reference the built-in social icons**

 
The four built-in icons ship as `static readonly RenderFragment` fields on [SocialIcons](https://usepennington.net/reference/blogsite/social-icons.md) — pass the field directly, not as a component tag (`SocialIcons.GithubIcon`, not `<SocialIcons.GithubIcon />`). Add a `using Pennington.BlogSite.Components;` directive at the top of `Program.cs` so `SocialIcons.GithubIcon` resolves.

 
**Add the `Socials` and `MainSiteLinks` blocks**

 
Add a `Socials = [...]` block with four entries covering all four built-ins, then a `MainSiteLinks = [...]` block with three entries — `Home` pointing to `/`, `Archive` to `/archive`, and `Tags` to `/tags`. Those URLs match the routes BlogSite includes, so the top-nav populates with no additional code.

 
```csharp:symbol,bodyonly
var builder = WebApplication.CreateBuilder(args);
  
builder.Services.AddBlogSite(() => new BlogSiteOptions
{
    SiteTitle = "Hero Blog",
    SiteDescription = "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();
```

 
> [!CHECKPOINT]
>  - Run `dotnet run` and visit `http://localhost:5000/`
>  - A horizontal row of four SVG icons — GitHub, Bluesky, LinkedIn, Mastodon — sits below the "My Work" sidebar card, each linking out to its `Url`
>  - 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.
 - All four homepage surfaces on `BlogSiteOptions` (hero, work, socials, header links) populate from one options block.
 
 
That completes the BlogSite getting-started arc — you have a scaffolded host, a fully-populated post, and a configured home page. From here, [populate the blog homepage](https://usepennington.net/how-to/theming/blogsite-homepage.md) is the how-to that revisits these same options when you need them on their own, [generate social card images](https://usepennington.net/how-to/feeds/social-cards.md) adds OpenGraph cards to every post, and [Built-in BlogSite routes](https://usepennington.net/reference/blogsite/routes.md) catalogs the full route surface the template serves.

 
[Previous
                
                Publish your first post and light up the RSS feed](https://usepennington.net/tutorials/blogsite/first-post.md)[Next
                    
                Add a second locale to your site](https://usepennington.net/tutorials/beyond-basics/add-a-locale.md)