Add a Razor landing page at the site root
Route a Razor component at / so a DocSite opens on a hand-built landing page, and swap the doc-page chrome for the sidebar-free FullWidthLayout.
By the end of this tutorial the DocSite at http://localhost:5000/ opens on a Razor landing page — a hero heading, a call to action, and two cards linking into the Guides area — laid out with the sidebar-free FullWidthLayout instead of the default doc-page chrome.
Along the way the tutorial covers routing a Razor component at /, why a @page "/" route wins over DocSite's catch-all, and swapping the layout a routed page renders inside.
Prerequisites
- .NET 10 SDK installed
- Completed Add doc pages and link between them — it provides the single-area host and the
install/configureguide pages this landing page links to
The finished code for this tutorial lives in examples/DocSitePagesAndLinksExample.
1. Clear the root so a Razor component can claim it
The host from Add doc pages and link between them binds one content area, guides, to the /guides/ tab. If you also followed Scaffold a documentation site with DocSite, a Content/index.md is serving the root — that's the markdown landing page this tutorial replaces with a routed Razor component. Delete it so the root is free, then confirm / returns a 404 before you route a component at it.
Delete Content/index.md
Remove the scaffold's root markdown page. With nothing bound to the root — no Content/index.md and no routed component pointed at / — a request to / returns a 404.
Run the host and visit the root
dotnet run
Open http://localhost:5000/ in a browser.
Checkpoint
http://localhost:5000/returns a 404 — nothing serves the root.http://localhost:5000/guides/still renders the Guides hub from Add doc pages and link between them.
2. Route a Razor component at the root
A routed Razor component whose @page template is / owns the root URL. AddDocSite adds your project's assembly to the routing assemblies it hands both the live Blazor router and the static build's page scanner, so a @page component in your project is picked up by both with no extra wiring. And a literal / route is more specific than DocSite's own catch-all, so it wins the match.
Create Components/Index.razor
Create a Components/ folder at the project root and add Index.razor with a @page "/" directive and minimal markup.
@page "/"
<h1>Pages & Links</h1>
<p>The site root now renders a Razor component.</p>
The @page "/" directive is the whole wiring — no Program.cs change, no registration call.
Restart the host
A .razor edit is a compile change, so stop the host and run dotnet run again to pick up the new component.
Checkpoint
http://localhost:5000/no longer 404s — it renders the Pages & Links heading.- The page is wrapped in the default doc-page chrome: a sidebar on the left and an outline rail on the right. The next unit replaces that layout.
3. Switch to the full-width layout
A routed component with no @layout directive renders inside DocSite's default, MainLayout — the three-column doc-page chrome with the sidebar and outline rail. A landing page wants the header and footer but not the navigation columns. FullWidthLayout is exactly that shape.
Add a @layout directive to Index.razor
Add one line under @page naming the layout by its full type name.
@page "/"
@layout Pennington.DocSite.Components.Layout.FullWidthLayout
<h1>Pages & Links</h1>
<p>The site root now renders a Razor component.</p>
FullWidthLayout keeps the DocSite header and footer and gives the page the full content width — no sidebar, no outline rail.
Restart the host
The @layout directive is another .razor edit, so stop the host and run dotnet run again to recompile.
Checkpoint
http://localhost:5000/renders the heading across the full content width.- The sidebar and outline rail are gone; the DocSite header and footer remain.
4. Build out the landing page
With routing and layout settled, the component is plain Razor markup. Fill it with a hero, a call to action, and two cards linking into the Guides area. Styling is MonorailCSS utility classes using the semantic palette — primary, accent, base — with a dark: variant on every color-bearing utility.
Replace Index.razor with the finished landing page
@page "/"
@layout Pennington.DocSite.Components.Layout.FullWidthLayout
@using Microsoft.AspNetCore.Components.Web
@* A Razor component named Index with @page "/" owns the root URL. DocSite
registers this project's assembly in its routing assemblies, so the literal
"/" route is picked up by both the live Blazor router and the static build —
and it beats the catch-all in Pages.razor. @layout swaps the sidebar layout
for FullWidthLayout, which keeps the header and footer but drops the nav. *@
<PageTitle>Pages & Links</PageTitle>
<section class="py-12 lg:py-20">
<p class="text-xs font-display font-semibold tracking-widest uppercase text-primary-600 dark:text-primary-400 mb-4">
Pennington DocSite
</p>
<h1 class="font-display text-4xl lg:text-6xl font-bold tracking-tight text-base-900 dark:text-base-50 leading-tight mb-6 text-balance">
A docs site with a front door.
</h1>
<p class="text-base lg:text-lg text-base-600 dark:text-base-400 max-w-2xl mb-8 leading-relaxed">
Every guide in one place — installing Pennington, configuring the host,
and the linking patterns that hold a documentation site together.
</p>
<div class="flex flex-wrap items-center gap-3">
<a href="/guides/"
class="inline-flex items-center font-display font-semibold bg-primary-600 hover:bg-primary-500 text-white rounded-xl px-6 py-3 transition-colors">
Read the guides
</a>
<a href="https://github.com/usepennington/pennington"
class="inline-flex items-center font-display font-semibold text-base-700 dark:text-base-200 hover:text-primary-700 dark:hover:text-primary-400 rounded-xl px-5 py-3 ring-1 ring-base-300 dark:ring-base-700 transition-colors">
View on GitHub
</a>
</div>
</section>
<section class="pb-16 lg:pb-24">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<a href="/guides/install"
class="rounded-2xl border border-base-200 dark:border-base-800 bg-base-50 dark:bg-base-900/50 p-6 transition-colors hover:border-primary-400 dark:hover:border-primary-500">
<h2 class="font-display text-xl font-semibold text-base-900 dark:text-base-50 mb-2">
Install Pennington
</h2>
<p class="text-sm text-base-600 dark:text-base-400 leading-relaxed">
Add the package and stand up the DocSite host.
</p>
</a>
<a href="/guides/configure"
class="rounded-2xl border border-base-200 dark:border-base-800 bg-base-50 dark:bg-base-900/50 p-6 transition-colors hover:border-primary-400 dark:hover:border-primary-500">
<h2 class="font-display text-xl font-semibold text-base-900 dark:text-base-50 mb-2">
Configure the site
</h2>
<p class="text-sm text-base-600 dark:text-base-400 leading-relaxed">
Set the title, footer, and area routing.
</p>
</a>
</div>
</section>
The two cards link to /guides/install and /guides/configure — the pages built in Add doc pages and link between them. <PageTitle> sets the browser tab text, the same component DocSite uses on doc pages.
Restart the host and open the root
Stop the host and run dotnet run again to recompile the component, then open http://localhost:5000/.
Checkpoint
http://localhost:5000/renders the hero heading, the Read the guides button, and two guide cards.- Clicking a card navigates to the matching guide page; the Read the guides button lands on
/guides/. - Run
dotnet run -- build— the static build's page scanner picks up the same@page "/"route and writes the landing page tooutput/index.html.
Summary
- A Razor component with
@page "/"owns the site root —AddDocSitealready routes your project's assembly, so the directive is the whole wiring. - A literal
/route beats DocSite's catch-all, and the same route is honored by both the live host and the static build. - A routed component defaults to
MainLayout; a@layoutdirective namingFullWidthLayoutdrops the sidebar for a landing-page shape. - The component body is ordinary Razor styled with MonorailCSS — semantic palette utilities,
dark:variants, and links straight into the content areas.