# Creating a New Page
Think of a page as a **form letter** — the template is the letter with blanks left for personalisation, and your C# class is the person who fills those blanks in before the letter is sent. The build system generates all the plumbing between the two; you just write the template and the class.
---
## What you want to achieve
By the end of this guide you will have a new page at a URL like `/dashboard` that:
- Shows your own custom HTML
- Loads instantly as a full page when you visit the URL directly
- Swaps in as a smooth partial update when navigated to from the sidebar
- Accepts data you pass to it from C#
---
## The two files every page needs
| File | What it is |
|---|---|
| `Templates/MyPage.htmx` | The letter template — HTML with `$$Slot$$` blanks |
| `Templates/MyPage.htmx.cs` | The person filling in the blanks — C# class + route handler |
The build system (`Htmx.SourceGenerator`) reads your `.htmx` file and generates an abstract C# class with one `RenderXxx()` method per `$$Slot$$`. Your job is to inherit that class and implement each method.
---
## Step 1 — Write the template
Create `Htmx.ApiDemo/Templates/MyPage.htmx`:
```html
$$Heading$$
$$Description$$
```
Rules for slots:
- Names are **PascalCase** surrounded by `$$` — e.g. `$$MySlot$$`
- A slot can contain plain text, HTML, or a rendered component
- The file must live inside `Templates/` so the build picks it up automatically
After saving this file and building, the generator emits `MyPageBase` — a class you will never edit but will inherit from.
---
## Step 2 — Write the code-behind
Create `Htmx.ApiDemo/Templates/MyPage.htmx.cs`:
```csharp
namespace Htmx.ApiDemo.Templates;
public sealed class MyPage : MyPageBase
{
private readonly byte[] _headingData;
private readonly byte[] _descriptionData;
public MyPage(string heading, string description)
{
// Convert strings to UTF-8 bytes once in the constructor.
// The Render methods then just write those bytes — no allocations at request time.
_headingData = heading.ToUtf8Bytes();
_descriptionData = description.ToUtf8Bytes();
}
protected override void RenderHeading(HtmxRenderContext ctx)
=> ctx.Writer.WriteUtf8(_headingData);
protected override void RenderDescription(HtmxRenderContext ctx)
=> ctx.Writer.WriteUtf8(_descriptionData);
}
```
The pattern here is deliberate: do all string work (formatting, encoding) in the constructor, so that `Render` is nothing but memory writes. This keeps request handling fast.
---
## Step 3 — Write the route handler
Route handlers live in the same `.htmx.cs` file. They are plain static methods registered with Minimal API — no special framework, no base class, no attributes from removed packages:
```csharp
namespace Htmx.ApiDemo.Templates;
public static class MyPageEndpoints
{
public static void Map(IEndpointRouteBuilder app)
{
app.MapGet("/my-page", Handle);
}
private static IResult Handle(HttpContext ctx)
{
var page = new MyPage(
heading: "My New Page",
description: "This is a minimal example."
);
ctx.WriteHtmxPage(page, title: "My Page");
return Results.Empty;
}
}
```
Then register it in `Program.cs` alongside the other endpoint registrations:
```csharp
MyPageEndpoints.Map(app);
```
---
## Step 4 — Add a sidebar link (optional but typical)
Open `Templates/MainLayout.htmx` and add a nav entry inside the `