# 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); //

RenderTitle(context.Next()); context.Writer.WriteUtf8(_part1); //

RenderBody(context.Next()); context.Writer.WriteUtf8(_part2); //

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