Built-in BlogSite routes
Catalog of the routes the Pennington.BlogSite package ships out of the box, keyed to the BlogSiteOptions knobs that control them.
UseBlogSite mounts the Razor pages that serve the homepage, archive, tag index, per-tag listing, and individual posts, plus an optional /rss.xml endpoint. Pages are discovered via RazorPageContentService; AddBlogSite registers Pennington.BlogSite as an additional routing assembly so Razor picks them up.
Entry point
UseBlogSite calls MapRazorComponents<App>, which discovers every @page-annotated component in Pennington.BlogSite.dll; when BlogSiteOptions.EnableRss is true it additionally maps a MapGet("/rss.xml", …) endpoint that returns the RSS feed. The /sitemap.xml endpoint is mounted by UsePennington via SitemapService (gated on PenningtonOptions.MapSitemap, which AddBlogSite mirrors from BlogSiteOptions.EnableSitemap).
Routes
Every route a BlogSite host serves, ordered by the surface they serve. Most are @page Razor components mapped by UseBlogSite, including the /{*path:nonfile} root catch-all; /sitemap.xml is the exception, mounted by UsePennington. UseBlogSite runs UsePennington before mapping the component endpoint, so redirectUrl: pages short-circuit with a redirect rather than falling through to the catch-all.
| Path | Method | Option controlling it | Description |
|---|---|---|---|
/ |
GET |
— (fixed) | Homepage Razor page (Home.razor); renders BlogSiteOptions.HeroContent, recent posts via BlogSummary, and sidebar modules from MyWork/Socials/AuthorBio. |
/archive |
GET |
— (fixed) | Full archive Razor page (Archive.razor); renders every post in reverse chronological order through BlogSummary. |
/archive/page/{Page:int} |
GET |
PostsPerPage |
Second @page directive on Archive.razor for paginated archive views; only renders extra pages when PostsPerPage > 0 and there are more posts than fit on one page. |
/tags |
GET |
— (fixed) | Tag index Razor page (Tags.razor); lists every tag with post counts, backed by a registered taxonomy axis (AddTaxonomy<BlogSiteFrontMatter, string>). |
/tags/{TagEncodedName} |
GET |
— (fixed) | Per-tag listing Razor page (Tag.razor); resolves the tag slug against the taxonomy axis and renders its posts through BlogPostsList. |
/blog/{*fileName:nonfile} |
GET |
BlogBaseUrl (see note) |
Post-rendering catch-all Razor page (Blog.razor); the :nonfile route constraint excludes paths that look like static files. Looks up the post by {BlogBaseUrl}/{fileName} and renders it through BlogPost with OpenGraph and structured-data head tags. |
/{*path:nonfile} |
GET |
— (fixed) | Root catch-all Razor page (Pages.razor); the lowest-priority route that handles any request matching no other page. Renders a content-root 404.md, then a host-provided NotFound component, then the built-in localized not-found message, and marks the response 404. The build's sentinel request also lands here, so whatever it renders becomes output/404.html. |
/rss.xml |
GET |
EnableRss |
MapGet endpoint in UseBlogSite that returns the RSS feed; omitted entirely when EnableRss is false. |
/sitemap.xml |
GET |
EnableSitemap |
MapGet endpoint mounted by UsePennington (not UseBlogSite); AddBlogSite forwards EnableSitemap into PenningtonOptions.MapSitemap to gate it. |
The @page directives on Tags.razor, Tag.razor, and Blog.razor are fixed string literals. /tags and /tags/{slug} are backed by a registered taxonomy axis (AddTaxonomy<BlogSiteFrontMatter, string>(BaseUrl = "/tags")), which supplies the term data and discovers the per-tag routes for the static build while the @page components render them with full site chrome. BlogBaseUrl only affects the post URLs Blog.razor resolves; the page route stays at /blog/{*fileName:nonfile} unless replacement Razor pages are supplied via AdditionalRoutingAssemblies.
Option-to-route matrix
BlogSiteOptions knobs that affect route registration or URL resolution, one row per option.
| Option | Default | Routes it affects | Effect |
|---|---|---|---|
BlogBaseUrl |
"/blog" |
/blog/{*fileName:nonfile} |
The URL prefix Blog.razor strips before resolving a post; must match the /blog/... literal in the page route (see the fixed-string-literal note above). |
EnableRss |
true |
/rss.xml |
Gates the MapGet("/rss.xml", …) call in UseBlogSite; when false the endpoint is not registered and the static crawler does not emit rss.xml. |
PostsPerPage |
10 |
/archive/page/{Page:int} |
Page size for the paginated archive routes. Set to 0 to disable pagination — all posts then render on the first page. |
EnableSitemap |
true |
/sitemap.xml (from UsePennington) |
Forwards into PenningtonOptions.MapSitemap; when false, UsePennington skips the /sitemap.xml MapGet and the static crawler omits it from the build output. |
Example
A BlogSite host that mounts every route above via a single UseBlogSite call.
using Pennington.BlogSite;
var builder = WebApplication.CreateBuilder(args);
// Swap the bare `AddPennington` host for the BlogSite template. `AddBlogSite`
// wires the full blog experience on top of Pennington core — a Blazor
// layout with a home page that lists recent posts, an /archive page,
// /blog/<slug> post pages, /tags and /tags/<name> listings, and an
// /rss.xml feed — all driven from `BlogSiteOptions`.
builder.Services.AddBlogSite(() => new BlogSiteOptions
{
SiteTitle = "Scaffold Blog",
SiteDescription = "A minimal BlogSite scaffold showing AddBlogSite, UseBlogSite, and RunBlogSiteAsync.",
CanonicalBaseUrl = "https://example.com",
// BlogSite defaults put posts under `{ContentRootPath}/{BlogContentPath}`
// (Content/Blog) and serves them at `BlogBaseUrl` (/blog); tag listings live
// at /tags. Overriding the defaults is as simple as setting the matching
// property — shown here with the defaults for clarity.
ContentRootPath = "Content",
BlogContentPath = "Blog",
BlogBaseUrl = "/blog",
// Author identity feeds into the RSS channel, JSON-LD article markup,
// and any post that omits its own `author:` front-matter value.
AuthorName = "Author Name",
AuthorBio = "Writing about software, tools, and the occasional side project.",
});
var app = builder.Build();
// `UseBlogSite` mounts antiforgery, static files, Razor component routing
// (Home/Archive/Blog/Tag/Tags live inside Pennington.BlogSite.dll), the
// MonorailCSS `/styles.css` endpoint, and the core Pennington middleware —
// all in the right order. When `EnableRss` is true (the default) it also
// maps `/rss.xml` so the static crawler picks the feed up.
app.UseBlogSite();
// `RunBlogSiteAsync` delegates to `RunOrBuildAsync`, so `dotnet run` serves the
// blog live and `dotnet run -- build <baseUrl> <outputDir>` generates static
// HTML. Both positional args are optional (defaults: `/` and `output`).
await app.RunBlogSiteAsync(args);
See also
- Reference:
BlogSiteOptions - Reference: Built-in
SocialIconsRenderFragments - How-to: Customize DocSite layouts and components
- How-to: Configure the BlogSite homepage