Use multiple content sources
When one markdown tree outgrows a single root — a /docs/ section alongside a separate /blog/ section, or a catch-all root paired with a specialised subtree — registering multiple content sources is the answer. The right recipe depends on the host: AddDocSite supports multiple folder-scoped sub-trees through ContentArea entries, while bare AddPennington allows any number of chained AddMarkdownContent<T> calls with independent front-matter types. For a first site, start with Add your first markdown page.
Assumptions
- A working Pennington site (see Your first Pennington site if not)
- The chosen host extension —
AddDocSitevs bareAddPennington— and the reason for that choice (When is DocSite the right starting point?) - Familiarity with
IFrontMatterbasics (Use a custom front-matter record)
For a working DocSite multi-area setup, see examples/DocSiteKitchenSinkExample. For the bare AddPennington chained-sources recipe, see examples/MultipleSourcesExample; the helpers on ServiceConfiguration back each step below.
Steps
- 1
Decide: DocSite areas, or chained
AddMarkdownContentcalls?AddDocSiteowns exactly one markdown pipeline keyed onDocSiteFrontMatter; useContentArea[]onDocSiteOptions.Areasto split it into folder-scoped sub-trees (continue to step 2). For two different front-matter types, or for a site already on bareAddPennington, chainAddMarkdownContent<T>calls instead (jump to step 4). - 2
(DocSite) Declare the areas
Each
ContentAreaslug becomes both the URL prefix and the top-level folder underContentRootPath.[ new ContentArea("Main", "main"), new ContentArea("API", "api"), ] - 3
(DocSite) Wire the areas onto
DocSiteOptions.AreasAssign the areas array when building
DocSiteOptions— the relevant property isAreas = BuildAreas().new() { SiteTitle = "Kitchen Sink Docs", Description = "A wide-surface DocSite example that backs eighteen how-to pages.", GitHubUrl = "https://github.com/usepennington/pennington", CanonicalBaseUrl = "https://example.com/", HeaderContent = """<a href="/" class="font-bold">Kitchen Sink Docs</a>""", FooterContent = BuildFooter(), ColorScheme = BuildColorScheme(), DisplayFontFamily = "'DocSiteKitchenSinkDisplay', system-ui, sans-serif", BodyFontFamily = "'DocSiteKitchenSinkBody', system-ui, sans-serif", FontPreloads = BuildFontPreloads(), ExtraStyles = BuildExtraStyles(), ConfigureLocalization = ConfigureLocalization, Areas = BuildAreas(), }Skip to Verify.
- 4
(Bare Pennington) Register the first markdown source
Call
AddMarkdownContent<TFrontMatter>insideAddPenningtonwith aContentPaththat roots the first tree, a distinctBasePageUrl, and an optionalSectionLabelto group the source's pages in navigation.md.ContentPath = "Content/docs"; md.BasePageUrl = "/docs"; md.SectionLabel = "Documentation"; - 5
(Bare Pennington) Register the second markdown source
Point the second
AddMarkdownContent<T>at a differentContentPathandBasePageUrl; the front-matter type can differ from the first source.md.ContentPath = "Content/blog"; md.BasePageUrl = "/blog"; md.SectionLabel = "Blog"; - 6
(Optional) Carve out an overlapping subtree with
ExcludePathsWhen one source's
ContentPathis a parent of another's, Pennington emits an overlap warning at startup because both pipelines would discover the inner tree and produce conflicting outputs. AddingExcludePathson the broader source gives the specialised source exclusive ownership of that subtree.md.ContentPath = "Content"; md.BasePageUrl = "/"; md.ExcludePaths = ["blog"];
Verify
- Run
dotnet runand visit each source'sBasePageUrl. Confirm pages render under both prefixes - Startup logs contain no
Markdown content source rooted at '…' overlaps…warnings (or, if expected, the warning text names the subtree intended for exclusion) - Each source's pages appear under the correct
SectionLabel/ContentArea.Titlein the generated navigation
Related
- Reference:
PenningtonOptions.AddMarkdownContent<T>(confirm path) - Reference:
DocSiteOptions.Areas/ContentArea(confirm path) - Background: When is DocSite the right starting point?
- Extensibility: Implement a custom content service