mostlylucid.pagingtaghelper 1.0.0

dotnet add package mostlylucid.pagingtaghelper --version 1.0.0
                    
NuGet\Install-Package mostlylucid.pagingtaghelper -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="mostlylucid.pagingtaghelper" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="mostlylucid.pagingtaghelper" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="mostlylucid.pagingtaghelper" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add mostlylucid.pagingtaghelper --version 1.0.0
                    
#r "nuget: mostlylucid.pagingtaghelper, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package mostlylucid.pagingtaghelper@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=mostlylucid.pagingtaghelper&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=mostlylucid.pagingtaghelper&version=1.0.0
                    
Install as a Cake Tool

Mostlylucid Paging TagHelper

NuGet .NET License

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" />
<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" />

Read the Custom Views Guide


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

Read the Localization Guide


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-pages to 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

TagHelper Guides

Features & Customization

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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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
Loading failed

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