mostlylucid.pagingtaghelper
1.0.0
dotnet add package mostlylucid.pagingtaghelper --version 1.0.0
NuGet\Install-Package mostlylucid.pagingtaghelper -Version 1.0.0
<PackageReference Include="mostlylucid.pagingtaghelper" Version="1.0.0" />
<PackageVersion Include="mostlylucid.pagingtaghelper" Version="1.0.0" />
<PackageReference Include="mostlylucid.pagingtaghelper" />
paket add mostlylucid.pagingtaghelper --version 1.0.0
#r "nuget: mostlylucid.pagingtaghelper, 1.0.0"
#:package mostlylucid.pagingtaghelper@1.0.0
#addin nuget:?package=mostlylucid.pagingtaghelper&version=1.0.0
#tool nuget:?package=mostlylucid.pagingtaghelper&version=1.0.0
Mostlylucid Paging TagHelper
The most flexible, powerful, and easy-to-use pagination library for ASP.NET Core.
v1.0 is 100% backward compatible! Upgrading from pre-v1 is safe and requires zero code changes. All existing attributes and defaults continue to work exactly as before. See migration guide
Built for modern web apps with HTMX, Alpine.js, or vanilla JavaScript support. Style it with TailwindCSS, DaisyUI, Bootstrap, or bring your own CSS. Zero configuration required to get started, infinitely customizable when you need it.
<paging model="Model" />
Live Demo | Documentation | Blog Posts
Features
Multiple UI Frameworks Out of the Box
- TailwindCSS + DaisyUI - Modern, beautiful components with dark mode
- Pure TailwindCSS - Utility-first styling without component dependencies
- Bootstrap - Classic Bootstrap pagination styles
- Plain CSS - Custom injected styles for any project
- Custom Views - Complete control with your own Razor views
Progressive Enhancement with JavaScript Modes
- HTMX - AJAX pagination without page reloads (default)
- HTMX + Alpine.js - Reactive state management with HTMX
- Alpine.js - Lightweight client-side navigation
- Plain JavaScript - Vanilla JS for maximum compatibility
- No JavaScript - Fully functional with forms and links only
Internationalization Built-In
- 8 languages supported out of the box (EN, DE, ES, FR, IT, JA, PT, ZH-Hans)
- Custom localization with resource files or inline text
language="fr"- Just set the culture- Custom summary templates with placeholders
Developer Experience
- Zero Configuration - Works immediately with sensible defaults
- Type-Safe - Full IntelliSense support in Razor views
- Extensible - Override anything, customize everything
- Well-Tested - 106 unit tests and counting
- HTMX-First - But works great without it
Bonus Features
- Continuation Token Pagination - Support for NoSQL databases (Cosmos DB, DynamoDB, Azure Table Storage) - Included in v1.0 with basic functionality, enhanced features coming in v1.1
- Sortable Headers - Flip between ascending/descending with visual indicators
- Page Size Selector - Standalone component for changing items per page
- Search Integration - Preserve search terms and filters across pages automatically
- Dark Mode - Automatic dark mode support for all views
Installation
dotnet add package mostlylucid.pagingtaghelper
That's it! No configuration files, no middleware, no setup. Just add the package and start using it.
Quick Start
1. Create Your Model
Implement IPagingModel with your data:
public class ProductListViewModel : IPagingModel
{
public int Page { get; set; } = 1;
public int PageSize { get; set; } = 10;
public int TotalItems { get; set; }
public List<Product> Products { get; set; } = new();
}
2. Populate in Your Controller
public async Task<IActionResult> Index(int page = 1, int pageSize = 10)
{
var totalProducts = await _db.Products.CountAsync();
var products = await _db.Products
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToListAsync();
var model = new ProductListViewModel
{
Page = page,
PageSize = pageSize,
TotalItems = totalProducts,
Products = products
};
return View(model);
}
3. Add to Your View
@model ProductListViewModel
<h1>Products</h1>
@foreach (var product in Model.Products)
{
<div>@product.Name</div>
}
<paging model="Model" />
Done! You now have fully-functional pagination with HTMX support, page size selection, and localized text.
Usage Examples
HTMX with Partial Updates
<div id="product-list">
<partial name="_ProductList" model="Model" />
</div>
<paging model="Model"
hx-target="#product-list"
hx-swap="outerHTML" />
Custom Language
<paging model="Model" language="fr" />
Custom Summary Template
<paging model="Model"
summary-template="Showing {startItem}-{endItem} of {totalItems} items" />
Pure Tailwind (No DaisyUI)
<paging model="Model" view-type="Tailwind" />
No JavaScript (Progressive Enhancement)
<paging model="Model" js-mode="NoJS" />
Custom Text
<paging model="Model"
first-page-text="First"
previous-page-text="Previous"
next-page-text="Next"
last-page-text="Last" />
With Search
<paging model="Model" search-term="@Model.SearchQuery" />
Styling
The tag helper includes several built-in view types:
| View Type | Description | Dark Mode |
|---|---|---|
TailwindAndDaisy |
TailwindCSS + DaisyUI components (default) | Yes |
Tailwind |
Pure Tailwind utility classes | Yes |
Bootstrap |
Bootstrap 5 pagination | No |
Plain |
Custom injected CSS | Yes |
NoJS |
Form-based, zero JavaScript | Yes |
<paging model="Model" view-type="Tailwind" />
Custom Views
Create your own view in Views/Shared/Components/Pager/:
<paging model="Model" view-type="Custom" use-local-view="true" />
JavaScript Modes
Control how pagination behaves on the client:
| Mode | Description | Requirements |
|---|---|---|
HTMX |
AJAX requests without page reloads | htmx.js |
HTMXWithAlpine |
HTMX + reactive state | htmx.js + alpine.js |
Alpine |
Client-side navigation | alpine.js |
PlainJS |
Vanilla JavaScript | None |
NoJS |
Forms and links only | None |
<paging model="Model" js-mode="Alpine" />
Read the JavaScript Modes Guide
Localization
Built-in Languages
Set the culture with one attribute:
<paging model="Model" language="de" />
<paging model="Model" language="ja" />
<paging model="Model" language="fr" />
Supported: English, German, Spanish, French, Italian, Japanese, Portuguese, Chinese (Simplified)
Custom Text
Override any text inline:
<paging model="Model"
first-page-text="First"
previous-page-text="Prev"
next-page-text="Next"
last-page-text="Last" />
Custom Summary Templates
Use placeholders to build your own summary:
<paging model="Model"
summary-template="Page {currentPage} of {totalPages} - {totalItems} results" />
Available Placeholders:
{currentPage}- Current page number{totalPages}- Total pages{totalItems}- Total item count{pageSize}- Items per page{startItem}- First item on page{endItem}- Last item on page
Bonus: Sortable Headers
Add sortable column headers with visual flip indicators:
<table>
<thead>
<tr>
<sortable-header column="Name"
current-order-by="@Model.OrderBy"
descending="@Model.Descending">
Product Name
</sortable-header>
<sortable-header column="Price"
current-order-by="@Model.OrderBy"
descending="@Model.Descending">
Price
</sortable-header>
</tr>
</thead>
</table>
Works seamlessly with HTMX or standard forms.
Bonus: Page Size Selector
Use the page size component standalone:
<page-size model="Model" />
<page-size model="Model" page-size-steps="5,10,25,50,100" />
<page-size model="Model" hx-target="#results" />
Continuation Pager (NoSQL Pagination)
For NoSQL databases that use continuation tokens (Cosmos DB, DynamoDB, Azure Table Storage):
<continuation-pager
model="Model"
htmx-target="#results-container"
show-page-number="true"
show-pagesize="true"
max-history-pages="20"
preserve-query-parameters="true" />
Key Features:
- Token-based navigation - Uses database continuation tokens instead of page offsets
- Numbered pages - Shows visited pages: [← Prev] [1] [2] [3 active] [4 disabled] [Next →]
- Backward navigation - Stores token history to enable clicking previous page numbers
- Query parameter preservation - Automatically preserves filters/searches (critical for token validity)
- Configurable history limit - Set
max-history-pagesto limit memory usage (default: 20)
Implement IContinuationPagingModel in your view model:
public class ProductsViewModel : IContinuationPagingModel
{
public string? NextPageToken { get; set; }
public bool HasMoreResults { get; set; }
public int PageSize { get; set; } = 25;
public int CurrentPage { get; set; } = 1;
public Dictionary<int, string>? PageTokenHistory { get; set; }
public List<Product> Products { get; set; } = new();
}
Documentation
Getting Started
- Getting Started - Detailed setup and first steps
- Migration Guide - Upgrading to v1.0 (100% backward compatible)
TagHelper Guides
- Pager TagHelper - Full pagination controls
- PageSize TagHelper - Standalone page size selector
- SortableHeader TagHelper - Sortable column headers
- ContinuationPager TagHelper - NoSQL token-based pagination
Features & Customization
- JavaScript Modes - HTMX, Alpine, PlainJS, NoJS modes
- Custom Views - Build your own pagination UI
- Localization - Multi-language support (8 languages)
- Advanced Usage - Search, sorting, filtering, performance
Reference
- API Reference - Complete attribute and property reference
Why Choose This Library?
For Beginners
- Works immediately with zero configuration
- Sensible defaults for everything
- Clear, simple API
For Professionals
- Full TypeScript/IntelliSense support
- 106 unit tests ensuring stability
- Backward compatible - won't break your code
- Production-ready and battle-tested
For Designers
- Multiple CSS frameworks supported
- Dark mode built-in
- Fully customizable views
- Accessible by default
For Performance
- Minimal JavaScript footprint
- Works without JavaScript entirely
- HTMX-optimized for instant navigation
- No heavy framework dependencies
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Acknowledgments
Built by Scott Galloway
- HTMX - For making AJAX simple again
- Alpine.js - For lightweight reactivity
- TailwindCSS - For utility-first styling
- DaisyUI - For beautiful components
Support
Made with ASP.NET Core Tag Helpers and View Components
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- JetBrains.Annotations (>= 2025.2.2)
- Microsoft.AspNetCore.Components.Web (>= 8.0.14)
-
net9.0
- JetBrains.Annotations (>= 2025.2.2)
- Microsoft.AspNetCore.Components.Web (>= 8.0.14)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 103 | 2/2/2026 |
| 1.0.0-rc5 | 93 | 2/2/2026 |
| 1.0.0-rc4 | 419 | 11/7/2025 |
| 1.0.0-rc2 | 130 | 11/7/2025 |
| 1.0.0-rc1 | 140 | 11/7/2025 |
| 0.9.4.1 | 407 | 4/27/2025 |
| 0.9.4 | 280 | 4/26/2025 |
| 0.9.2 | 157 | 4/26/2025 |
| 0.9.1 | 981 | 3/24/2025 |
| 0.9.0 | 169 | 3/21/2025 |
| 0.8.9 | 146 | 3/21/2025 |
| 0.8.8 | 210 | 3/20/2025 |
| 0.8.7 | 216 | 3/20/2025 |
| 0.8.6 | 194 | 3/20/2025 |
| 0.7.1 | 216 | 3/20/2025 |
| 0.7.0 | 215 | 3/20/2025 |
| 0.6.3 | 219 | 3/18/2025 |
| 0.6.2 | 201 | 3/18/2025 |
| 0.6.1 | 204 | 3/17/2025 |
| 0.6.0 | 204 | 3/17/2025 |
1.0.0
MAJOR UPDATE - Breaking changes and new features:
NEW FEATURES:
- ContinuationPager TagHelper for NoSQL databases (Cosmos DB, DynamoDB, Azure Table Storage) - WORK IN PROGRESS (will be completed in v1.1)
* Numbered page navigation showing visited pages with clickable buttons
* Token history management with configurable max limit (default: 20 pages)
* Automatic query parameter preservation (filters, searches, sorts)
* Support for all JavaScript modes (HTMX, Alpine, PlainJS, NoJS)
* Forward/backward navigation using continuation tokens
- JavaScript Modes: js-mode attribute with 5 options (HTMX, HTMXWithAlpine, Alpine, PlainJS, NoJS)
- Pure Tailwind View: No DaisyUI dependency. TailwindAndDaisy now fully uses DaisyUI components
- NoJS View: Zero-JavaScript using forms and links. Perfect for accessibility
- Custom Summary Templates with placeholders
- URL Parameter Preservation: All query parameters automatically preserved across pagination
UNIFIED CSS FRAMEWORK ADAPTER:
- CssFrameworkAdapter centralizes all CSS classes for each framework (DaisyUI, Tailwind, Bootstrap, Plain)
- Single Default.cshtml per component adapts to any CSS framework via adapter
- JsModeHelper centralizes JavaScript mode attribute generation for navigation buttons
- Bootstrap 5: Proper <nav><ul class="pagination"><li class="page-item"><a class="page-link"> structure
- PreferAnchorTags flag ensures Bootstrap gets <a> tags instead of <button> for correct styling
SORTABLE HEADER UX OVERHAUL:
- New model attribute: <sortable-header column="Name" model="Model"> auto-extracts OrderBy/Descending
- Reduces per-column boilerplate from ~13 attributes to 2 (column + model)
- js-mode attribute for consistency with pager's JavaScript mode system
- Falls back to current request path when no explicit URL source provided
- Full backward compatibility - all existing attributes still work
PAGE JUMP INPUT:
- show-page-jump="true" attribute adds "Go to page:" input with Go button
- Works with all JS modes (NoJS uses form, HTMX uses htmx.ajax, PlainJS uses window.location)
- Styled per framework via CssFrameworkAdapter (PageJumpContainer, PageJumpInput, PageJumpButton)
RESPONSIVE COLLAPSE:
- Page numbers automatically hidden on small screens, showing only prev/next
- DaisyUI/Tailwind: hidden sm:inline-flex (requires explicit CSS rule in consuming app)
- Bootstrap: d-none d-sm-block
- Active page always visible regardless of screen size
LOCALIZATION:
- 14 languages: EN, FR, DE, ES, IT, PT, JA, ZH, KO, AR, RU, NL, PL, TR
- New localized strings: ChangeButtonText, GoToPageLabel, GoButtonText,
GoToPageAriaLabel, PageIndicatorText, PageNavigationAriaLabel
- All hardcoded strings in views replaced with localizer fallbacks
ARIA ACCESSIBILITY:
- aria-current="page" on active page buttons
- aria-disabled="true" on disabled elements
- Localized aria-label on nav elements
- Proper active/disabled state via boolean flags (not CSS class comparison)
CONTINUATION PAGER FEATURES:
- preserve-query-parameters attribute (default: true) - Preserves all URL parameters except pager's own
- max-history-pages attribute (default: 20) - Limits stored continuation tokens
- enable-token-accumulation attribute - Controls token history storage
- Numbered page display showing: [Prev] [1] [2] [3 active] [4 disabled] [Next]
- Parameter prefix support for multiple pagers on same page
- Dark mode support with proper background colors
PROGRESSIVE ENHANCEMENT:
- PageSize select in JS modes now wrapped in <form> with <noscript> submit button
- When JavaScript is disabled, a "Change" button appears for form-based page size submission
- Form preserves existing query parameters for seamless degradation
- NoJS view type continues to show "Change" button unconditionally
IMPROVEMENTS:
- DaisyUI active page now uses btn-primary for clear visual distinction (was btn-active)
- Updated to HTMX 2.0.4, DaisyUI 5.4.7, webpack 5.102.1
- Comprehensive documentation and 196+ passing unit tests
- Fixed dark mode background colors in sample application
- Enhanced token validity by preserving query context
- Bootstrap demo page with proper HTMX partial rendering
BUG FIXES:
- Fixed namespace typos causing Docker build failures
- Fixed styling consistency across view types
- Fixed dark mode toggle and Alpine.js Razor compatibility
- Fixed dark mode page background not changing (bg-gray-900)
- Fixed Bootstrap pagination using <button> instead of <a> tags
- Fixed aria-current/aria-disabled applied to all Bootstrap items (CSS class comparison bug)
BREAKING CHANGES:
- use-htmx deprecated. Use js-mode instead
- ViewType.TailwindAndDaisy now uses DaisyUI. Use ViewType.Tailwind for pure Tailwind
- Individual view files (BootstrapView.cshtml, TailwindView.cshtml, etc.) replaced by unified Default.cshtml per component
0.9.4.1 - Remove daisyui select-primary class from page size dropdown
0.9.4 - Show page size control when no paging
0.9.2 - Fix totalitems=0 not showing paging controls
0.9.1 - Reset page to 1 on page size change
0.9.0 - Enhanced paging control visibility
0.8.9 - MaxPageSize property, fix sorting table headers
0.8.7 - Improved pagesize postback
0.8.5 - Build fix for Logical Resource Names
0.8.0 - Separate PageSize tag helper, preserve query string params
0.7.1 - Preserve query params in page size dropdown
0.7.0 - Preserve extra query parameters when paging
0.6.1 - Return href plain when AutoAppend=false
0.6.0 - HTMX-free FlippySortTagHelper
0.5.1 - Update samples page
0.5.0 - Add FlippySortTagHelper (SortableHeaderTagHelper)
0.4.0 - Initial public release
0.3.1 - Improve sample layouts
0.3.0 - Add light mode for all views
0.2.0 - Multiple views: TailwindAndDaisy, Custom, Plain, Bootstrap. Add @Html.PlainCSS()
0.1.1 - Update README
0.1.0 - Add JavaScript snippet for Onchange
0.0.4 - Sample fixes
0.0.3 - Update build script
0.0.2 - Initial pre-release