Lists & Pagination¶
Status: Canonical Applies to: Discussions, Sessions, Repositories, Activity feeds, Admin lists
Purpose¶
Lists are measuring instruments, not decorative UI.
They must:
- Communicate scope -- how much exists
- Preserve user control -- loading is always explicit
- Remain precise under scale -- no misleading partial views
- Avoid exposing implementation details -- no pages, offsets, or cursors
SageOx adopts a Linear x Apple x MonarchMoney philosophy: Reveal data deliberately. Never paginate by nostalgia.
Core Principles¶
1. Pagination communicates scope, not mechanics¶
Users should always understand:
- How much exists
- How much they've seen
- What happens if they ask for more
Avoid exposing implementation details (pages, offsets, cursors).
2. User intent always controls loading¶
- Tables do not auto-load on scroll
- Loading more is an explicit action
- Scroll position must be preserved
3. Sorting must be truthful¶
- Sorting with partial data is misleading
- If data is paginated, sorting must be server-side
- Changing sort resets pagination
Pagination Pattern (Canonical)¶
Disallowed Patterns¶
| Pattern | Reason |
|---|---|
| Numbered pages (1 2 3) | Feels like 2015 admin software |
| Auto infinite scroll | Loses position, breaks CMD+F, hides scope |
| Client-side sort on partial data | Produces incorrect results |
| Page-size selectors | Enterprise noise |
Approved Pattern: Explicit "Load More"¶
+--------------------------------------------+
| 42 recordings Sort: Newest v |
+--------------------------------------------+
| * Ready Weekly Sync 12m 34s 1d |
| * Ready Sprint Retro 8m 12s 3d |
| * Processing Design Review 2m ago |
| ... |
+--------------------------------------------+
| Showing 20 of 42 . Load more |
+--------------------------------------------+
Key characteristics:
- Explicit user action -- nothing loads without a click
- Clear progress -- "Showing X of Y" at all times
- No page numbers -- scope is communicated, not mechanics
- No auto-fetch -- scroll position stays where the user left it
Pagination Mechanics¶
Server Requirements¶
- Pagination may be offset-based or cursor-based
- API must return
totalandhas_more - Ordering must be deterministic
Client Behavior¶
- Initial load: 20-50 items
- "Load more" fetches next slice
- Append results; preserve scroll position
- When
has_more = false, footer becomes passive
Sorting Rules (Critical)¶
Canonical Rules¶
- Sorting is server-side only
- Only one active sort at a time
- Changing sort:
- Resets pagination
- Scrolls list to top
- Clears previously loaded pages
Client-side sorting with partial data is prohibited.
Sort UI Placement¶
- Primary: Toolbar dropdown
- Secondary (optional): Column headers (must sync with toolbar)
If both exist:
- Toolbar is the source of truth
- Column clicks update toolbar state
Approved Sort Options (Discussions)¶
| Option | Default |
|---|---|
| Newest | Yes |
| Oldest | |
| Duration | |
| Title |
Shared Components (Required)¶
1. ListToolbar¶
Purpose: Grounds the list. Communicates scope. Houses sort/filter controls.
Layout:
- Single row, no heavy borders
- Left: total count
- Right: sort / filters
Props:
interface ListToolbarProps {
total: number
sortOptions: SortOption[]
currentSort: SortOption
onSortChange: (sort: SortOption) => void
filterSlot?: ReactNode
}
2. LoadMoreFooter¶
Purpose: Replaces traditional pagination. Communicates progress + intent.
States:
| State | Copy |
|---|---|
| Can load more | Showing 20 of 42 . Load more |
| Loading | (spinner) |
| Fully loaded | All 42 recordings loaded |
Props:
interface LoadMoreFooterProps {
loaded: number
total: number
hasMore: boolean
loading: boolean
onLoadMore: () => void
}
3. DataTable -- Server Pagination Mode¶
The existing DataTable must support a server-driven mode.
interface DataTableServerPagination {
total: number
loaded: number
hasMore: boolean
isLoadingMore: boolean
onLoadMore: () => void
}
When enabled:
- Client-side pagination is disabled
- Footer pagination is rendered instead
Alignment & Density Rules¶
Column Alignment¶
| Content Type | Alignment |
|---|---|
| Text | Left-aligned |
| Numbers / durations / dates | Right-aligned |
Headers must align with column values.
Column Weight¶
- Columns that are often empty must be demoted
- Status columns should be compact (dot or badge)
Density¶
- Default: medium density
- High-density allowed for activity-heavy lists
- Lists should never feel taller than their data
Empty & Sparse States¶
- Sparse data should collapse padding slightly
- Whitespace must not exaggerate absence
- Empty states should explain what will appear here, not how to use the app
See UI Density & Confidence for card-level empty state rules.
Checklist¶
Before shipping a list, ask:
- Can the user tell how much exists?
- Can they load more only if they choose to?
- Does sorting produce globally correct results?
- Are numeric columns right-aligned?
- Are internal IDs or redundant labels exposed?
If any answer is wrong, the list is not done.
Non-Negotiables¶
| Rule | Rationale |
|---|---|
| No numbered pagination | Exposes mechanics, not scope |
| No auto infinite scroll for tables | Loses position, hides scope |
Always show progress (Showing X of Y) |
Users deserve to know where they are |
| Sorting resets pagination | Partial-data sorts produce lies |
| Server-side sorting only | Client sorts on partial data are incorrect |
| Explicit user control | Nothing loads without intent |
SageOx Standard¶
Lists in SageOx reveal information progressively, honestly, and calmly. Pagination exists to build trust, not to move pages.
Related¶
- UX Lint Checklist: Lists & Pagination -- Executable enforcement checklist (must pass before merge)
- UI Density & Confidence -- Card structure, empty states, spacing
- Brand in Product -- Terminology and copy patterns
- For Product Engineers -- Token reference and integration