Skip to content

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 total and has_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:

  1. Can the user tell how much exists?
  2. Can they load more only if they choose to?
  3. Does sorting produce globally correct results?
  4. Are numeric columns right-aligned?
  5. 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.