Sorting & pagination
MonkeyTab can sort and paginate data two ways: client-side (the component does it internally) and server-side (you control it via props and fetch from your backend).
Sorting
Client-side (default)
Click a column header to sort. No props needed — MonkeyTab sorts the rows array in memory.
Server-side (controlled)
Pass sortBy, sortDirection, and onSortChange to hand sorting control to your backend:
const [sortBy, setSortBy] = useState<string | null>(null);
const [sortDir, setSortDir] = useState<'asc' | 'desc' | null>(null);
// Your data fetch, re-runs when sort changes
const { data } = useQuery(['rows', sortBy, sortDir], () =>
api.getRows({ sort: sortBy, dir: sortDir })
);
<MonkeyTable
columns={columns}
rows={data}
sortBy={sortBy}
sortDirection={sortDir}
onSortChange={(field, dir) => {
setSortBy(field);
setSortDir(dir);
}}
/>When sortBy is set, clicking a header calls onSortChange instead of sorting in memory. The column header shows the sort indicator.
Type-aware sort labels
Sort menu labels adapt to the column type:
| Type | Ascending label | Descending label |
|---|---|---|
| Text | A → Z | Z → A |
| Number | 0 → 9 | 9 → 0 |
| Date | Oldest first | Newest first |
| Boolean | False first | True first |
| Rating | Low → High | High → Low |
Filtering
The filter panel (toolbar → filter button) lets users build type-aware filter conditions.
Client-side (default)
Filters run on the rows array in memory. Nothing to configure.
Server-side
To apply filters on the server, read the current filter state from the onFilterChange callback (if you've wired it up) or listen for onCellChange/etc. For now, the most common pattern for server-side filtering is to add filter controls outside the grid and pass a pre-filtered rows prop.
Pagination
Client-side (default)
When totalRows is not set, the grid renders all rows from the rows array. Use pageSize to limit how many are shown:
// No pagination props — renders all rows
<MonkeyTable columns={columns} rows={rows} />Server-side pagination
Set totalRows to enable pagination controls. Manage page yourself:
const [page, setPage] = useState(1);
const pageSize = 100;
const { data, totalCount, isLoading } = useQuery(
['rows', page],
() => api.getRows({ page, limit: pageSize })
);
<MonkeyTable
columns={columns}
rows={data ?? []}
totalRows={totalCount}
page={page}
pageSize={pageSize}
onPageChange={(p) => setPage(p)}
paginationMode="simple"
paginationLoading={isLoading}
/>Pagination modes
| Mode | UI |
|---|---|
'simple' (default) |
Previous / Next buttons with "Showing X–Y of Z" |
'load-more' |
A single "Load more" button that appends rows |
<MonkeyTable
paginationMode="load-more"
...
/>Combining sort + filter + pagination
When doing everything server-side, wire all three together and reset page to 1 on sort or filter changes:
const [page, setPage] = useState(1);
const [sortBy, setSortBy] = useState<string | null>(null);
const [sortDir, setSortDir] = useState<'asc' | 'desc' | null>(null);
const { data, total, isLoading } = useQuery(
['rows', page, sortBy, sortDir],
() => api.getRows({ page, limit: 50, sort: sortBy, dir: sortDir })
);
<MonkeyTable
columns={columns}
rows={data ?? []}
// Pagination
totalRows={total}
page={page}
pageSize={50}
onPageChange={setPage}
paginationLoading={isLoading}
// Sorting
sortBy={sortBy}
sortDirection={sortDir}
onSortChange={(field, dir) => {
setSortBy(field);
setSortDir(dir);
setPage(1); // reset to first page on sort change
}}
/>Ghost grid
When height is a fixed pixel value, empty space below the last row looks blank. The ghost grid fills that space with faint placeholder rows and columns — it's on by default for fixed-height tables.
// Auto-on when height is a number
<MonkeyTable height={500} ... />
// Explicit: choose how many ghost rows/columns
<MonkeyTable height={500} ghostGrid={{ rows: 8, columns: 3 }} ... />
// Turn off
<MonkeyTable height={500} ghostGrid={false} ... />Ghost column headers create a real column when clicked; ghost row cells create a new row (when editable and allowCreateRecord are true).
Height modes
| Value | Behavior |
|---|---|
'auto' |
Fits content — height grows with rows, no scrollbar for small tables |
number (px) |
Fixed height — vertical scrolling when rows overflow |
CSS string ('100%', '50vh') |
Fills a parent element |
Pair height="auto" with maxHeight to cap growth:
<MonkeyTable height="auto" maxHeight={600} ... />