Data Tables and Pagination
Relevant source files
The following files were used as context for generating this wiki page:
- packages/web/src/components/sortable-header.tsx
- packages/web/src/components/table-pagination.tsx
- packages/web/src/components/ui/table.tsx
- packages/web/src/routes/_authed/admin/users/index.tsx
- packages/web/src/routes/_authed/admin/customers/index.tsx
- packages/web/src/features/users/users.functions.ts
- packages/api.tests/Users/UserServiceTests.cs
- packages/web/src/api/schema.ts
Introduction
The "Data Tables and Pagination" system in this project provides a standardized framework for displaying, filtering, and navigating large datasets within the administrative interface. It utilizes a combination of TanStack Router for URL-state synchronization, Shadcn/UI for consistent visual presentation, and backend services optimized for paged data retrieval.
This system ensures that high-volume entities—such as users, customers, and contracts—are handled efficiently by loading only the necessary records for the current view. The implementation leverages URL search parameters to maintain the state of search queries, sort orders, and page numbers, enabling deep-linking and a consistent browser history experience.
Sources: packages/web/src/routes/_authed/admin/users/index.tsx:23-30, packages/web/src/routes/_authed/admin/customers/index.tsx:23-30
Architecture and Components
Frontend Component Hierarchy
The data table architecture is composed of several reusable components that interact with TanStack Router's search state.
| Component | Description |
|---|---|
Table | The base container utilizing Shadcn/UI styles for semantic HTML table rendering. |
SortableHeader | A specialized header cell that triggers sort state changes and displays active sort indicators. |
TablePagination | A footer component providing navigation controls and record count summaries. |
Input (Search) | A debounced input field used to filter results via the search URL parameter. |
Sources: packages/web/src/components/ui/table.tsx:5-10, packages/web/src/routes/_authed/admin/users/index.tsx:10-14
Data Flow for Listing Entities
The following diagram illustrates how search parameters are synchronized between the UI, the router, and the backend API.
The data flow relies on the URL as the source of truth for the table's state.
Sources: packages/web/src/routes/_authed/admin/users/index.tsx:23-57, packages/web/src/features/users/users.functions.ts:8-19
Sorting Logic
Sorting is implemented by appending a hyphen (-) to a column identifier for descending order, while the identifier alone indicates ascending order.
- Logic: If the active sort column is clicked again, it toggles between ascending (e.g.,
name) and descending (e.g.,name-). - Implementation: The
toggleSortfunction in route files handles this string manipulation before callingnavigate. - Server Processing: Server functions parse these "slugs" into a column enum and a
SortDirection('Asc' or 'Desc') before calling the API.
Sources: packages/web/src/routes/_authed/admin/users/index.tsx:49-57, packages/web/src/features/users/users.functions.ts:11-18
Pagination Implementation
Pagination is controlled by a standardized PAGE_SIZE of 25 records.
The totalPages is calculated on the client side using Math.ceil(total / PAGE_SIZE).
Sources: packages/web/src/routes/_authed/admin/users/index.tsx:21-41, packages/web/src/components/table-pagination.tsx
Backend Pagination and Search
The backend (C#) handles the actual filtering and slicing of data via the UserService and similar modules.
API Schema
API endpoints for paged data follow a consistent pattern for query parameters:
| Parameter | Type | Description |
|---|---|---|
search | string | Optional text search for names, emails, or specific fields. |
sort | Enum | The field to sort by (e.g., Name, Email, Role). |
sortDirection | Enum | Asc or Desc. |
page | integer | The current page number (1-indexed). |
pageSize | integer | Number of items per page. |
Sources: packages/web/src/api/schema.ts:167-175, packages/api.tests/Users/UserServiceTests.cs:105-113
Server Logic Example
The UserService applies filters to the IQueryable collection before executing the query against the database.
// Logic demonstrated in tests
var result = await service.GetAllAsync("alice", UserSort.Name, SortDirection.Asc, 1, 25);
// result.Total contains the count of all matching records
// result.Items contains the subset for the requested page
Sources: packages/api.tests/Users/UserServiceTests.cs:46-53, packages/api.tests/Users/UserServiceTests.cs:105-113
Summary
Data Tables and Pagination in the project are built for scale and usability. By centralizing state in the URL through TanStack Router and providing consistent UI components for headers and pagination, the system ensures a unified administrative experience. The backend complements this with efficient paged queries that include total record counts for accurate pagination controls.
Sources: packages/web/src/routes/_authed/admin/users/index.tsx