# Pagination A row of numbered page links — Previous, 1, 2, 3…, Next. Use it at the bottom of a list or table when there are too many items to show all at once. You give it the current page number, the total number of pages, and a URL pattern; it builds all the links automatically. --- ## Quick example ```csharp new Pagination(current: 3, total: 10, urlPattern: "/blog?page={0}") ``` This renders a navigation row with links to pages 1–10 (with ellipsis for interior pages) and the Previous/Next arrows. --- ## All the options ```csharp public Pagination( int current, int total, string urlPattern) ``` | Parameter | What it does | |---|---| | `current` | The currently active page. 1-based (the first page is `1`). | | `total` | The total number of pages. | | `urlPattern` | A URL with `{0}` where the page number goes. E.g. `"/items?page={0}"`. | The visible page window is at most 7 buttons. For large page counts, interior pages collapse into ellipsis (`…`) while the first page, last page, and pages close to `current` stay visible. --- ## Real-world examples ### Basic list with pagination ```html
$$Posts$$
$$Pager$$ ``` ```csharp // Templates/BlogPage.htmx.cs _pager = new Pagination( current: page, total: totalPages, urlPattern: "/blog?page={0}"); ``` ### Preserving filters and sort order across pages Build the URL pattern to include any query parameters that should survive page navigation: ```csharp var urlPattern = $"/users?role={role}&sort={sort}&page={{0}}"; new Pagination(current: page, total: totalPages, urlPattern: urlPattern) ``` > Note the double braces `{{0}}` to produce a literal `{0}` after string interpolation. ### HTMX-powered pagination (no full page reload) Wrap the pagination (and the content it controls) in a `hx-boost` container: ```html
$$Items$$
$$Pager$$
``` --- ## How it works All links are plain `` elements — no JavaScript required. The URL for each page is built by calling `string.Format(urlPattern, pageNumber)`. When `current == 1`, the Previous link is styled as disabled (pointer-events removed, opacity reduced); same for Next when `current == total`. --- ## Complete page example **`Templates/BlogPage.htmx`** ```html

Blog

$$PostList$$
$$Pagination$$
``` **`Templates/BlogPage.htmx.cs`** ```csharp namespace Htmx.ApiDemo.Templates; public sealed class BlogPage : BlogPageBase { private readonly byte[] _postList; private readonly IHtmxComponent _pagination; public BlogPage(IEnumerable posts, int currentPage, int totalPages) { var sb = new System.Text.StringBuilder(); foreach (var post in posts) { sb.Append($"""

{System.Net.WebUtility.HtmlEncode(post.Title)}

{post.PublishedAt:MMMM d, yyyy}

{System.Net.WebUtility.HtmlEncode(post.Summary)}

"""); } _postList = sb.ToString().ToUtf8Bytes(); _pagination = new Components.Pagination( current: currentPage, total: totalPages, urlPattern: "/blog?page={0}"); } protected override void RenderPostList(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_postList); protected override void RenderPagination(HtmxRenderContext ctx) => _pagination.Render(ctx.Next()); } ``` **GET handler** ```csharp [Handler] [MapGet("/blog")] public static partial class GetBlogHandler { public record Query([property: FromQuery] int Page = 1); private static async Task HandleAsync( Query q, HttpContext ctx, BlogService blog, CancellationToken ct) { const int pageSize = 10; var (posts, total) = await blog.GetPageAsync(q.Page, pageSize, ct); int totalPages = (int)Math.Ceiling(total / (double)pageSize); return await ctx.WriteHtmxPage( new BlogPage(posts, q.Page, totalPages), title: "Blog"); } } ```