A static content engine for .NET
A static site your .NET project deserves.
Without leaving the SDK.
Markdown in, static site out. No Node, no npm, no bundler — just dotnet run.
Pennington ships content at the speed of your build.
One ASP.NET server, two outputs — a live, hot-reloading site while you write, and the flat HTML you ship when you build. The getting-started tutorial wires it up from dotnet new web.
Quickstart
Add Pennington to an ASP.NET project.
Start from dotnet new web, add the package, and wire AddPennington. The tutorial walks every line.
Create an ASP.NET host
Add the Pennington package
Wire AddPennington, then run
Familiar on purpose
Pick up where your dotnet habits already are.
No new CLI to learn, no build tool to babysit. Open the solution, hit F5, and you're shipping content. Your layouts are Razor components. Your config is a C# record. Your deploy target is whatever you'd deploy an ASP.NET app to.
Your docs are a project in the same solution. Add a <ProjectReference> to the code you document — samples and API reference come straight from the source.
A POCO with real types. IntelliSense, refactoring, and compile errors instead of silent YAML typos.
Dev mode is a live ASP.NET app with hot reload. Build mode crawls its own routes and writes flat files. Same code, two outputs.
The dev loop
Edit. Save. See it.
Run dotnet run once. Markdown, front matter, navigation, cross-references, and Razor components all hot-reload. The feedback loop is the one you already trust for web apps — pointed at your content.
--- title: Release notes order: 1 --- ## Shipping v1.2 Now with <Badge Variant="Success">faster builds</Badge> and a rewritten navigation tree.
Shipping v1.2
Now with faster builds and a rewritten navigation tree.
Nav
Tree rebuilt
Search
Index refreshed
Xrefs
Resolved
Content-first
The shell stays out of your way.
Layout, navigation, search, dark mode — handled. Your attention belongs on the content: Razor components inside your markdown, syntax highlighting from the same grammars your editor uses, and an API reference generated from your assemblies’ XML docs.
Your components. In your markdown.
Write a Razor component. Drop it in prose. Rendered at build time, no JS bundle required.
## API Status The ingest endpoint is <Badge Variant="Warning">beta</Badge>. Read the <ApiLink Id="T:Ingest.Client" /> for the current shape.
Pull real source into your docs.
Point at a file — or a single method by name — and the real declaration is pulled straight from your repo at build time. The sample can’t drift out of date, because it is the source.
```csharp:symbol,bodyonly src/BlogSiteServiceExtensions.cs > BlogSiteServiceExtensions.AddBlogSite ```
services.AddSingleton<IBlogContentService, BlogContentService>(); services.AddSingleton<BlogHomeRouteGenerator>(); services.AddSingleton<BlogArchiveRouteGenerator>(); services.AddSingleton<TagRouteGenerator>(); // … return services;
Utility CSS, generated
Write Tailwind-style classes in your Razor and markdown. MonorailCSS scans them and emits exactly the CSS you used — in C#, at build time. No Tailwind CLI, no PostCSS, no purge step.
Rich Markdown
Tabbed code blocks, Mermaid, alerts, diff highlights, and focus lines. Shipped. Configurable.
All the bells and whistles
Search, RSS, sitemap, structured data, llms.txt, locale routing — all built in. Sensible defaults; reach for config only when you want it.
Where to next
Four doors in. Pick one.
The docs are split by what you’re trying to do: learn, solve, understand, or look something up.
Tutorials
Build it alongside us.
Step through a working site, one piece at a time.
How-to
Solve one thing.
Short recipes to get unstuck and back to work.
Explanation
See how it fits together.
Context and reasoning behind the moving parts.
Reference
Look it up.
The dry, complete listings — for when you need a name.