---
title: Scaffold a blog with BlogSite
description: "Swap the bare Pennington host for the BlogSite template and configure the core options that drive the home, archive, post, tag, and RSS routes."
canonical_url: https://usepennington.net/tutorials/blogsite/scaffold/
sidecar_url: https://usepennington.net/tutorials/blogsite/scaffold.md
content_hash: sha256:1b2f1ef87f115145ae4223fa9952c1cf1bc2cbde00069d62eb8f804de2eb0187
tokens: 1917
uid: tutorials.blogsite.scaffold
reading_time_minutes: 4
---

Getting Started
# Scaffold a blog with BlogSite

Swap the bare Pennington host for the BlogSite template and configure the core options that drive the home, archive, post, tag, and RSS routes.

 
By the end of this tutorial, a running BlogSite host titled "Scaffold Blog" serves a home listing, `/archive`, `/blog/<slug>/`, `/tags/`, `/tags/<name>/`, and `/rss.xml` — all from a single placeholder post under `Content/Blog/`.

 
`AddBlogSite` folds the host, layout, navigation, and styling into one call, configured for a site where the blog *is* the site; for what the template wires, where the wiring stops, and why DocSite and BlogSite can't share an app, read [what the templates wire for you](https://usepennington.net/explanation/positioning/docsite-positioning.md) first.

 
## Prerequisites

 
 - .NET 10 SDK installed
 - Completed [Create your first Pennington site](https://usepennington.net/tutorials/getting-started/first-site.md)
 - Completed [Serve markdown through a Blazor catch-all](https://usepennington.net/tutorials/getting-started/first-page.md) (so `Content/` already has at least one markdown file)
 
 
The stable .NET 10 SDK is all BlogSite needs — its package targets .NET 10, and you never write the `union` keyword that would call for a preview SDK. See [the SDK and the union shim](https://usepennington.net/explanation/positioning/sdk-and-the-union-shim.md) for when the .NET 11 beta is worth opting into.

 
The finished code for this tutorial lives in [examples/BlogSiteScaffoldExample](https://github.com/usepennington/pennington/tree/main/examples/BlogSiteScaffoldExample).

 
---

 
## 1. Start from the bare Pennington host

 
The host you built in the getting-started tutorials calls `AddPennington`, registers content with `AddMarkdownContent<DocFrontMatter>`, mounts `UsePennington`, and routes through a Blazor `@page "/{*Path}"` catch-all (`MarkdownPage.razor`) that resolves each URL with `IPageResolver` to serve individual pages.

 
That host serves individual pages but nothing else: no home listing, no `/archive`, no `/blog/<slug>` pages, no `/tags` listings, no `/rss.xml` feed, and no [MonorailCSS](https://monorailcss.github.io/MonorailCss.Framework/) chrome. The next section brings all of that in with a single `AddBlogSite` call.

 
**Add the first post**

 
Posts live under `{ContentRootPath}/{BlogContentPath}` — `Content/Blog/` with the defaults. Add one placeholder post so the host has something to serve. It uses four front-matter keys — `title`, `description`, `date`, and `author` — the minimum the home listing and RSS feed will need once the template is wired:

 
```markdown:symbol
---
title: Hello world
description: A placeholder post so the scaffold has something to render. Tutorial 1.3.20 teaches the real BlogSiteFrontMatter fields.
date: 2026-04-13
author: Author Name
tags:
  - scaffold
---
  
This post exists so the bare BlogSite scaffold has at least one entry on the
home page and in the RSS feed. The next tutorial, **Author your first post
with `BlogSiteFrontMatter`**, walks through the full set of post front-matter
fields (tags, series, repository, section, redirectUrl, and more).
```

 
> [!CHECKPOINT]
>  - Run `dotnet run` and visit `/blog/hello-world` at the URL the console prints — the page shows unstyled HTML for the markdown. None of the BlogSite chrome (home listing, archive, tag pages, RSS) exists yet.

---

 
## 2. Wire `AddBlogSite`, `UseBlogSite`, and `RunBlogSiteAsync`

 
`AddBlogSite` is the BlogSite template's single registration call; it stands in for `AddPennington` plus the `AddMarkdownContent<DocFrontMatter>` line, wiring Pennington core, MonorailCSS, the Razor chrome, and the blog content services in one call. `UseBlogSite` mounts the middleware stack and Razor component routes; `RunBlogSiteAsync` dispatches between dev-serve and static-build.

 
**Replace `Program.cs` with the BlogSite calls**

 
```csharp:symbol,bodyonly
var builder = WebApplication.CreateBuilder(args);
  
builder.Services.AddBlogSite(() => new BlogSiteOptions
{
    SiteTitle = "Scaffold Blog",
    SiteDescription = "A minimal BlogSite scaffold showing AddBlogSite, UseBlogSite, and RunBlogSiteAsync.",
    CanonicalBaseUrl = "https://example.com",
  
    ContentRootPath = "Content",
    BlogContentPath = "Blog",
    BlogBaseUrl = "/blog",
  
    AuthorName = "Author Name",
    AuthorBio = "Writing about software, tools, and the occasional side project.",
});
  
var app = builder.Build();
  
app.UseBlogSite(); 
  
await app.RunBlogSiteAsync(args); 
```

 
The options populated here cover site identity (`SiteTitle`, `SiteDescription`, `CanonicalBaseUrl`), content paths shown at their defaults (`ContentRootPath`, `BlogContentPath`, `BlogBaseUrl`), and author fallbacks (`AuthorName`, `AuthorBio`). The full surface lives in [Pennington.BlogSite.BlogSiteOptions](https://usepennington.net/reference/api/blog-site-options.md).

 
> [!CHECKPOINT]
>  - Run `dotnet run` and visit `/` at the URL the console prints
>  - The BlogSite home layout appears: site title "Scaffold Blog", a recent-posts list with one entry, header chrome, and MonorailCSS styling

---

 
## 3. Verify every built-in route

 
With the post from section 1 in place and `AddBlogSite` wired, every route the template ships now responds. The placeholder post carries `tags: [scaffold]`, so the tag routes have one entry to list.

 
**Walk the page routes**

 
With the host running, visit each of these at the URL the console prints. Every one returns 200:

 
 - `/` — home listing with `hello-world` as the only recent post.
 - `/archive` — full archive, same single post in reverse-chronological order.
 - `/blog/hello-world` — the post itself, now rendered with BlogSite chrome.
 - `/tags` — the tag index, showing `scaffold` with a count of one.
 - `/tags/scaffold` — the per-tag listing with the one post.
 
 
**Check the RSS feed**

 
Visit `/rss.xml`. It returns `application/rss+xml` with one `<item>` carrying the post title, link, description, pub date, and author.

 
 
The full route surface, including the paginated `/archive/page/{n}` and per-tag pages, is cataloged in [Built-in BlogSite routes](https://usepennington.net/reference/blogsite/routes.md). The next tutorial expands the post to the full `BlogSiteFrontMatter` surface, adding `tags`, `series`, `repository`, `sectionLabel`, and `redirectUrl`.

 
> [!CHECKPOINT]
>  - Each page route above returns 200 and renders the placeholder post's metadata
>  - `/rss.xml` returns `application/rss+xml` content with one item whose `<guid>` matches the canonical post URL

---

 
## Summary

 
 - The bare `AddPennington` host gave way to `AddBlogSite` + `UseBlogSite` + `RunBlogSiteAsync`, and the full BlogSite chrome now renders.
 - The core `BlogSiteOptions` surface — `SiteTitle`, `SiteDescription`, `CanonicalBaseUrl`, `ContentRootPath`, `BlogContentPath`, `BlogBaseUrl`, `AuthorName`, `AuthorBio` — is populated, and each field flows through to the rendered output.
 - BlogSite binds posts through `AddMarkdownContent<BlogSiteFrontMatter>` (introduced in the next tutorial) and defaults content paths to `Content/Blog` served at `/blog`, which distinguishes it from the `DocSite` template's area-driven layout.
 - Every built-in route the template ships responds: `/`, `/archive`, `/blog/<slug>`, `/tags`, `/tags/<name>`, and `/rss.xml`.
 
 
[Previous
                
                Add a blog to your documentation site](https://usepennington.net/tutorials/docsite/add-a-blog.md)[Next
                    
                Publish your first post and light up the RSS feed](https://usepennington.net/tutorials/blogsite/first-post.md)