# Creating a New Page
This guide explains the full lifecycle of adding a new page to the app: the template file, the code-behind class, the handler, and the sidebar link.
## How pages work
Every page is a pair of files:
| File | Purpose |
|---|---|
| `Templates/MyPage.htmx` | HTML markup with `$$SlotName$$` slots |
| `Templates/MyPage.htmx.cs` | C# class that fills slots + declares the Minimal API handler |
The Roslyn source generator (`Htmx.SourceGenerator`) reads every `.htmx` file at build time and generates an abstract base class for it. You then write a concrete class in the companion `.htmx.cs` file that inherits from that base.
## How `$$SlotName$$` becomes code
Take this simple template:
```html
$$Title$$
$$Body$$
```
The generator splits the file on `$$...$$` patterns and produces:
```csharp
// auto-generated — do NOT edit
public abstract partial class MyPageBase : IHtmxComponent
{
protected abstract void RenderTitle(HtmxRenderContext context);
protected abstract void RenderBody(HtmxRenderContext context);
// static HTML segments stored as ReadOnlySpan for zero-allocation output
private static ReadOnlySpan _part0 => new byte[] { ... };
private static ReadOnlySpan _part1 => new byte[] { ... };
private static ReadOnlySpan _part2 => new byte[] { ... };
public void Render(HtmxRenderContext context)
{
context.Writer.WriteUtf8(_part0); //
}
}
```
Your job is to write the concrete class that implements each `RenderXxx` method.
## Step 1 — Create the `.htmx` template
Create `Htmx.ApiDemo/Templates/MyPage.htmx`:
```html
$$Heading$$
$$Description$$
```
Rules:
- Slot names are **PascalCase** and surrounded by `$$` — e.g. `$$MySlot$$`
- A slot can hold plain text, HTML, or another rendered component
- The file must be saved in `Templates/` (or a subfolder) so the `.csproj` `AdditionalFiles` glob picks it up
## Step 2 — Create the `.htmx.cs` code-behind
Create `Htmx.ApiDemo/Templates/MyPage.htmx.cs`:
```csharp
using Immediate.Apis.Shared;
using Immediate.Handlers.Shared;
namespace Htmx.ApiDemo.Templates;
// Concrete template — inherits from the generated base
public sealed class MyPage : MyPageBase
{
private byte[] _headingData = [];
private byte[] _descriptionData = [];
// Use `init`-only setters to pre-encode strings to UTF-8 bytes once
public required string Heading { init => _headingData = value.ToUtf8Bytes(); }
public required string Description { init => _descriptionData = value.ToUtf8Bytes(); }
protected override void RenderHeading(HtmxRenderContext context)
=> context.Writer.WriteUtf8(_headingData);
protected override void RenderDescription(HtmxRenderContext context)
=> context.Writer.WriteUtf8(_descriptionData);
}
// Minimal API handler — discovered and registered by the source generator
[Handler]
[MapGet("/my-page")]
public static partial class GetMyPageHandler
{
public record Query; // add route/query parameters here if needed
private static ValueTask HandleAsync(
Query query,
IHttpContextAccessor httpContextAccessor,
CancellationToken token)
{
var ctx = httpContextAccessor.HttpContext
?? throw new InvalidOperationException("HttpContext is not available.");
var page = new MyPage
{
Heading = "My New Page",
Description = "This is a minimal example page."
};
// WriteHtmxPage: full HTML shell for direct browser loads,
// bare fragment for HTMX partial swaps (HX-Request header present)
ctx.WriteHtmxPage(page, title: "My Page", appName: "HtmxApp", pageTitle: "My Page");
return ValueTask.CompletedTask;
}
}
```
## Step 3 — Add a sidebar link (optional but typical)
Open `Templates/MainLayout.htmx` and add a nav entry inside the `