Smart Table (Unified)
One Smart Table platform for design-system UI tables and CMS admin tables — headless controller, shared pagination, schema filters, and server/client modes.
On this page
Goal: one table stack for backend CMS surfaces (entry-list, media, users) and frontend design-system tables (ui/table) — less duplication, more flexibility, performance, features, and security.
P0 building blocks (implemented)
| Piece | Location | Role |
|---|---|---|
| Controller | createSmartTable |
Selection, sort, page, density, virtual, pin order, layout prefs |
| Shell | smart-table-shell.svelte |
Toolbar slot, empty/loading, scroll body, CMS pagination |
| Empty / loading | smart-table-empty / smart-table-loading |
Shared WCAG states |
| Chrome | chrome.ts |
Class tokens + pinCellClass / alignCellClass |
| Layout prefs | layout-prefs.ts |
localStorage density / order / visibility (layoutKey) |
| Column meta | SmartTableColumn.pin, .align, .meta |
TanStack-style defs without TanStack dependency |
| Column resize | setColumnWidth + column-resize-handle |
Drag widths, persisted with layoutKey |
| Saved views | @utils/smart-table-saved-views + menu UI |
Named filter/sort/layout presets (localStorage) |
| Status facets | smart-table-status-facets + getStatusFacets |
Server status counts as filter chips |
| List metrics | @utils/list-query-metrics + metrics badge |
Latency + cache hit; UI via ?debug=table (SSR snapshot) |
Entry-list UX (P1 wired)
| Control | Where | Behavior |
|---|---|---|
| Views menu | Toolbar (desktop + mobile expand) | Save/apply/delete named views (filters, search, sort, pageSize, layout) |
| Status facets | Above table | Chip filters → ?filter_status= via createSmartFilter (server FLAC) |
| Metrics badge | Toolbar when ?debug=table |
p50/p95/hit% from SSR listMetrics (server ring buffer) |
| Column resize | Header drag handles | setColumnWidth + per-collection layoutKey: entry-list:{collectionId} |
<SmartTableShell
empty={rows.length === 0}
emptyTitle="No results"
{currentPage} {rowsPerPage} {pagesCount} {totalItems}
onUpdatePage={(p) => table.setPage(p)}
onUpdateRowsPerPage={(n) => table.setPageSize(n)}
>
<table class={SMART_TABLE}>…</table>
</SmartTableShell>
Architecture
flowchart TB
subgraph Headless["Headless (shared)"]
CST[createSmartTable]
CSF[createSmartFilter]
CFE[collection-filter-engine]
end
subgraph UI["Presentation"]
Shell[smart-table-shell]
UItable[ui/table.svelte]
Pag[ui/table/pagination]
Toolbar[system/table-filter + ui/table/filter]
end
subgraph Adapters["Domain adapters"]
EL[entry-list]
Media[media-table]
Users[admin-area users]
Generic[any ui/table consumer]
end
CST --> UItable
CST --> EL
CSF --> EL
CFE --> EL
Pag --> EL
Pag --> UItable
Pag --> Media
Toolbar --> EL
| Layer | Location | Owns |
|---|---|---|
| Headless table | @components/ui/smart-table |
Selection, sort, density, page, virtualization |
| Headless filters | createSmartFilter + collection-filter-engine |
Schema widgets, FLAC, QueryBuilder IR, cache hash |
| Pagination | @components/ui/table/pagination |
One primitive (variant="simple" \| "cms") |
| CMS shell | entry-list.svelte |
Collection actions, preload, multibutton, plugins |
| Design-system shell | ui/table.svelte |
Generic data grid, client or server props |
Modes
| Mode | Use when | Sort / page / filter |
|---|---|---|
server |
Collection lists, media, large multi-tenant data | URL + SSR + CollectionService (secure) |
client |
Small admin widgets, settings tables | In-memory in createSmartTable |
// CMS / entry-list (server mode)
const table = createSmartTable({
mode: "server",
onQueryChange: (updates) => updateURL(updates),
getRowId: (row) => String(row._id),
});
table.setRows(serverEntries);
table.setPaginationMeta(serverPagination);
// Design system (client mode)
const table = createSmartTable({ mode: "client", pageSize: 10 });
table.setRows(localRows);
What was unified
| Before | After |
|---|---|
system/table/table-pagination full copy |
Thin wrapper → ui/table/pagination variant="cms" |
| Duplicate virtualization in entry-list + ui/table | createSmartTable.virtual |
| Index-based selection in entry-list | Id-based selection (safe with virtual rows) |
Weak TableController class ×2 |
createSmartTable + legacy re-export |
| Filters only in entry-list | Platform filter engine reusable by any server table |
Security (CMS tables)
Filtering/sorting must not be trusted from the client alone:
- UI sends
?filter_*/sort/page parseCollectionListQuery+ schema whitelistcompileSecureFilters+ FLACapplyFiltersToQueryBuilderportable IR- Cache key uses compiled hash + user id
See Collection Filtering Platform.
Migration guide
| Consumer | Action |
|---|---|
| New tables | createSmartTable + shared chrome tokens |
| entry-list | createSmartTable (server) + createSmartFilter + chrome |
| media-table | createSmartTable (client) + chrome + column sort |
| admin-area | createSmartTable (server) + onQueryChange → /api/user | /api/token |
| website-tokens | createSmartTable (server) + onQueryChange → /api/website-tokens |
| tenants admin | Shared chrome (SSR list) |
Legacy TableController |
Deprecated; re-exported for compatibility |
Other <table> UIs (not full smart-table yet): permissions matrix, media details mini-tables, migration wizard field map, accessibility help, ui/table.svelte design shell.
Shared visual chrome
import {
createSmartTable,
SMART_TABLE,
SMART_TABLE_THEAD,
SMART_TABLE_TH,
SMART_TABLE_TD,
SMART_TABLE_SCROLL,
SMART_TABLE_PAGINATION_BAR,
SMART_TABLE_TOOLBAR,
SMART_TABLE_ROW_SELECTED,
SMART_TABLE_ROW_HOVER,
} from "@components/ui/smart-table";
Import map
// Preferred
import { createSmartTable } from "@components/ui/smart-table";
import Pagination from "@components/ui/table/pagination.svelte";
// CMS-stable aliases (still valid)
import TablePagination from "@components/system/table/table-pagination.svelte"; // → ui pagination cms
Performance
- Virtualization threshold:
VIRTUALIZATION_THRESHOLD(25) via@utils/table-constants - Server mode: only current page in memory; SWR on service layer
- Client mode: local sort/page without network
- Id selection avoids virtual-index desync bugs
Related
Last Updated: 2026-07-15
Was this page helpful?