Shared Layout
Provide a consistent site shell (nav + header + footer) used by all pages via _ViewStart.cshtml.
Full source
File: pwsh/tutorial/examples/11.1-RazorPages.ps1
<#
Sample Kestrun Server showing PowerShell-backed Razor Pages.
This example demonstrates enabling the Razor Pages runtime and serving pages from a root folder.
FileName: 11.1-RazorPages.ps1
#>
param(
[int]$Port = 5000,
[IPAddress]$IPAddress = [IPAddress]::Loopback
)
# Initialize Kestrun root directory
# the default value is $PWD
# This is recommended in order to use relative paths without issues
Initialize-KrRoot -Path $PSScriptRoot
# Create a new logger
New-KrLogger |
Set-KrLoggerLevel -Value Debug |
Add-KrSinkConsole |
Register-KrLogger -SetAsDefault -Name 'DefaultLogger'
# Create a new Kestrun server
New-KrServer -Name 'RazorPages'
# Add a listener on the configured port and IP address
Add-KrEndpoint -Port $Port -IPAddress $IPAddress
# Add a Razor Pages handler to the server
Add-KrPowerShellRazorPagesRuntime -RootPath './Assets/Pages'
# Application-wide metadata (AVAILABLE TO ALL RUNSPACES)
$AppInfo = [pscustomobject]@{
Name = 'Kestrun Razor Demo'
Environment = 'Development'
StartedUtc = [DateTime]::UtcNow
Version = Get-KrVersion -AsString
}
Write-KrLog -Level Information -Message "Starting Kestrun RazorPages server '{name}' version {version} in {environment} environment on {ipaddress}:{port}" `
-Values $AppInfo.Name, $AppInfo.Version, $AppInfo.Environment, $IPAddress, $Port
# Define feature flags for the application
$FeatureFlags = @{
RazorPages = $true
Cancellation = $true
HotReload = $false
}
Write-KrLog -Level Information -Message 'Feature Flags: {featureflags}' -Values $($FeatureFlags | ConvertTo-Json -Depth 3)
# Define a Message of the Day (MOTD) accessible to all pages
$Motd = @'
Welcome to Kestrun.
This message comes from the main server script.
Defined once, visible everywhere.
'@
Write-KrLog -Level Information -Message 'Message of the Day: {motd}' -Values $Motd
# Enable Kestrun configuration
Enable-KrConfiguration
# Start the server asynchronously
Start-KrServer
File: pwsh/tutorial/examples/Assets/Pages/Shared/_Layout.cshtml
@{
// IMPORTANT:
// Do NOT set Layout here (layouts must not have layouts)
// Do NOT assume a model exists
var title = ViewData["Title"] as string ?? "Kestrun • Razor + PowerShell";
}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>@title</title>
<style>
body {
margin: 0;
font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
background: #0b1020;
color: #e7eaf3;
}
header {
background: rgba(255, 255, 255, 0.06);
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
padding: 1rem 1.25rem;
}
main {
max-width: 1000px;
margin: 0 auto;
padding: 1.5rem;
}
nav a {
margin-right: 1rem;
color: #9ad1ff;
text-decoration: none;
}
nav a:hover {
text-decoration: underline;
}
footer {
margin-top: 3rem;
padding-top: 1rem;
border-top: 1px solid rgba(255, 255, 255, 0.08);
color: rgba(231, 234, 243, 0.7);
font-size: 0.9rem;
}
.card {
background: rgba(255, 255, 255, 0.06);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 10px;
padding: 1rem;
margin-bottom: 1rem;
}
code,
pre {
background: rgba(0, 0, 0, 0.35);
padding: 0.25rem 0.4rem;
border-radius: 6px;
}
pre {
padding: 0.8rem;
overflow-x: auto;
}
</style>
</head>
<body>
<header>
<div style="max-width:1000px; margin:0 auto; display:flex; justify-content:space-between; align-items:center;">
<div>
<strong>Kestrun</strong><br />
<span style="font-size:0.85rem; opacity:0.8;">
Razor Pages + PowerShell
</span>
</div>
<nav>
<a href="@Context.Request.PathBase/">Home</a>
<a href="@Context.Request.PathBase/About">About</a>
<a href="@Context.Request.PathBase/Status">Status</a>
<a href="@Context.Request.PathBase/Quotes">Quotes</a>
<a href="@Context.Request.PathBase/Contact">Contact</a>
<a href="@Context.Request.PathBase/AppInfo">App Info</a>
<a href="@Context.Request.PathBase/Cancel">Cancel</a>
</nav>
</div>
</header>
<main>
@RenderBody()
<footer>
Served at @DateTime.UtcNow.ToString("u")
</footer>
</main>
</body>
</html>
Step-by-step
- Title: Read
ViewData["Title"]and fall back to a default. - Styling: Define minimal CSS for the demo.
- Navigation: Render links to each sample page.
- Body: Render the current page with
@RenderBody(). - Footer: Display a simple UTC timestamp.
Try it
Any page uses this layout (unless it sets Layout = null):
curl -i http://127.0.0.1:5000/
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Layout not applied | _ViewStart.cshtml not setting Layout | Ensure Assets/Pages/_ViewStart.cshtml sets Layout = "Shared/_Layout" |
| Links don’t work when hosted under a path base | Hard-coded absolute URLs | This layout uses @Context.Request.PathBase to prefix links |