Files
Htmx/docs/Components/Pagination.md
T
2026-05-05 23:55:26 +05:00

156 lines
4.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 110 (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
<!-- Templates/BlogPage.htmx -->
<div class="space-y-6 mb-10">$$Posts$$</div>
$$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
<div hx-boost="true" hx-target="#item-list" hx-push-url="true">
<div id="item-list">$$Items$$</div>
$$Pager$$
</div>
```
---
## How it works
All links are plain `<a href="...">` 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
<div class="max-w-3xl mx-auto py-10">
<h1 class="text-2xl font-bold mb-6">Blog</h1>
<div class="space-y-6 mb-10">
$$PostList$$
</div>
$$Pagination$$
</div>
```
**`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<BlogPost> posts, int currentPage, int totalPages)
{
var sb = new System.Text.StringBuilder();
foreach (var post in posts)
{
sb.Append($"""
<article class="border-b pb-6">
<h2 class="text-lg font-semibold mb-1">
<a href="/blog/{System.Net.WebUtility.HtmlEncode(post.Slug)}"
class="hover:underline">
{System.Net.WebUtility.HtmlEncode(post.Title)}
</a>
</h2>
<p class="text-sm text-muted-foreground mb-2">{post.PublishedAt:MMMM d, yyyy}</p>
<p class="text-sm">{System.Net.WebUtility.HtmlEncode(post.Summary)}</p>
</article>
""");
}
_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<IResult> 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");
}
}
```