# 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 `