The Pagination class manages paginated views with automatic route generation and state management. It follows the same pattern as StepFlow for consistent API design.
Key features: - Automatic pagination math (total pages, page ranges) - Auto-generated routes via create_router() - Query parameter preservation (filters, sorting, etc.) - Flexible data loading and rendering - HTMX integration for SPA-like navigation
def Pagination( pagination_id:str, # Unique identifier for this pagination instance data_loader:Callable, # Function that returns all items render_items:Callable, # Function to render items for a page items_per_page:int=20, # Number of items per page container_id:str=None, # HTML ID for container (auto-generated if None) content_id:str=None, # HTML ID for content area (auto-generated if None) preserve_params:List=None, # Query parameters to preserve style:PaginationStyle=<PaginationStyle.SIMPLE: 'simple'>, # Pagination display style prev_text:str='« Previous', # Text for previous button next_text:str='Next »', # Text for next button page_info_format:str='Page {current} of {total}', # Format for page info button_size:str=None, # Button size class push_url:bool=True, # Whether to update URL with hx-push-url show_endpoints:bool=False, # Whether to show First/Last buttons first_text:str='«« First', # Text for first page button last_text:str='Last »»', # Text for last page button redirect_route:Optional=None, # Route to redirect non-HTMX requests):
Manage paginated views with automatic route generation and state management.
def render_navigation_controls( current_page:int, # Current page number total_pages:int, # Total number of pages route_func:Callable, # Function to generate route for page)->FT: # Navigation controls element
def render_page_content( page_items:List, # Items for current page current_page:int, # Current page number total_pages:int, # Total number of pages request:Any, # FastHTML request object route_func:Callable, # Function to generate route for page)->FT: # Complete page content with items and navigation
Render complete page content with items and pagination controls.
def create_router( prefix:str='', # URL prefix for routes (e.g., "/library"))->APIRouter: # APIRouter with generated routes
Create FastHTML router with generated routes for pagination.
Usage Example
Here’s a complete example showing how to create a paginated view:
# Example: Simple paginated item listfrom fasthtml.common import Div, H3, P, Li, Ul# Define data loader functiondef load_items(request):"""Load all items - this could query a database, file system, etc."""# For demo, generate 100 itemsreturn [f"Item {i}"for i inrange(1, 101)]# Define render function for itemsdef render_items_list(items, page, request):"""Render items for current page."""return Ul(*[Li(item) for item in items], cls="list-disc ml-6" )# Create pagination instanceitems_pagination = Pagination( pagination_id="items", data_loader=load_items, render_items=render_items_list, items_per_page=10)# Generate routeritems_router = items_pagination.create_router(prefix="/items")# In your FastHTML app, register the router:# from cjm_fasthtml_app_core.core.routing import register_routes# register_routes(app, items_router)## Or directly:# items_router.to_app(app)# Access the pagination at: /items/content?page=1# Test pagination mathprint(f"Items per page: {items_pagination.items_per_page}")print(f"Total pages for 100 items: {items_pagination.get_total_pages(100)}")print(f"Total pages for 95 items: {items_pagination.get_total_pages(95)}")# Test page itemsall_items = load_items(None)page1_items, start, end = items_pagination.get_page_items(all_items, 1)print(f"\nPage 1: items {start+1}-{end} = {page1_items[:3]}...")page5_items, start, end = items_pagination.get_page_items(all_items, 5)print(f"Page 5: items {start+1}-{end} = {page5_items[:3]}...")
Items per page: 10
Total pages for 100 items: 10
Total pages for 95 items: 10
Page 1: items 1-10 = ['Item 1', 'Item 2', 'Item 3']...
Page 5: items 41-50 = ['Item 41', 'Item 42', 'Item 43']...
# Example 2: Pagination with preserved query parameters (filters, search, etc.)from fasthtml.common import Div, H3, P# Define data loader that uses query parametersdef load_media_files(request):"""Load media files, filtered by query parameters."""# In real app, this would filter based on request.query_params media_type = request.query_params.get("media_type", "all") if request else"all" view = request.query_params.get("view", "grid") if request else"grid"# Simulate filtered results all_files = [f"file{i}.mp4"for i inrange(1, 51)]# Return filtered files (simplified for demo)return all_files# Define render function that uses query parametersdef render_media_grid(items, page, request):"""Render media items in grid view.""" media_type = request.query_params.get("media_type", "all") if request else"all" view = request.query_params.get("view", "grid") if request else"grid"return Div( H3(f"Media Library ({view} view, filter: {media_type})"), P(f"Showing {len(items)} files on page {page}"), Div(*[Div(item) for item in items]) )# Create pagination with preserved parametersmedia_pagination = Pagination( pagination_id="media", data_loader=load_media_files, render_items=render_media_grid, items_per_page=20, preserve_params=["view", "media_type"], # Automatically preserve these in URLs style=PaginationStyle.SIMPLE)# Generate routermedia_router = media_pagination.create_router(prefix="/library")# Now when users navigate pages, view and media_type are automatically preserved:# /library/content?page=1&view=grid&media_type=video# /library/content?page=2&view=grid&media_type=video <- view and media_type preserved!print("Media pagination configured with parameter preservation:")print(f" Preserved params: {media_pagination.preserve_params}")print(f" Items per page: {media_pagination.items_per_page}")print(f" Style: {media_pagination.style.value}")
Media pagination configured with parameter preservation:
Preserved params: ['view', 'media_type']
Items per page: 20
Style: simple
Integration with FastHTML Applications
The Pagination class integrates seamlessly with FastHTML applications using the same pattern as StepFlow.
# This cell has been replaced by the new Pagination class above
Router Registration
After creating a Pagination instance and generating its router, register it with your FastHTML app:
from cjm_fasthtml_app_core.core.routing import register_routes# Create paginationlibrary_pagination = Pagination( pagination_id="library", data_loader=load_library_items, render_items=render_library_grid, items_per_page=20, preserve_params=["view", "media_type"])# Generate routerlibrary_router = library_pagination.create_router(prefix="/library")# Register with appregister_routes(app, library_router)# Or directly:# library_router.to_app(app)
The router automatically handles: - Pagination math (total pages, item slicing) - Query parameter preservation - Route generation with preserved state - HTMX integration
Loads all items based on request (can use query parameters for filtering):
def load_items(request):"""Load all items - can filter based on query parameters."""# Access filters from query parameters media_type = request.query_params.get("media_type", "all") search = request.query_params.get("search", "")# Load and filter items items = get_all_items()if media_type !="all": items = [item for item in items if item.type== media_type]if search: items = [item for item in items if search.lower() in item.name.lower()]return items
Render Function
Renders the items for display (receives current page items, page number, and request):
def render_items(items, page, request):"""Render items for current page."""# Can access query parameters if needed view_mode = request.query_params.get("view", "grid")if view_mode =="grid":return render_grid(items)else:return render_list(items)
The pagination automatically: - Calls data_loader to get all items - Slices items for current page - Passes page items to render_items - Adds navigation controls - Preserves specified query parameters
HTMX Integration
The Pagination class automatically configures HTMX attributes for SPA-like navigation:
hx-get: Fetches the new page content from the generated route
hx-target: Targets the content ID (auto-generated or custom)
hx-swap: Uses outerHTML to replace the entire content container
hx-push-url: Updates browser URL (configurable via push_url parameter)
href: Provides fallback for non-JavaScript navigation
Automatic Swap Strategy
The pagination uses outerHTML swap by default, which means: - The route returns a complete Div with id=content_id - HTMX replaces the entire element (including the ID) - This ensures the navigation controls are updated with each page change
Disabled States
Navigation buttons automatically handle boundary conditions: - Previous button disabled on page 1 (uses btn_behaviors.disabled) - Next button disabled on last page - Disabled buttons use DaisyUI styling
URL Management
When push_url=True (default): - Browser URL updates as users navigate pages - Users can bookmark specific pages - Back/forward buttons work correctly - Preserved query parameters are maintained in URLs
Example URL progression:
/library/content?page=1&view=grid&media_type=video
/library/content?page=2&view=grid&media_type=video # page changed, other params preserved
/library/content?page=3&view=grid&media_type=video