Named list configurations — “My open deals”, “Probation ending”, “Partners at risk” — as tabs above any list. A view stores filters, sort, columns and density; edits show a Modified strip with Save, Save as new and Reset. Personal or team scope, a default view, share links with ?sv=, and a dropdown form for narrow headers. Persistence stays in your app.
Installation
The whole kit as one package — get updates by bumping the version. Add the styles to globals.css: @import "@vamos-ui/react/styles.css";
Usage
import { SavedViews } from "@vamos-ui/react/data-table";
<SavedViews
views={views}
activeId={activeId}
current={currentViewState}
onSelect={setActiveId}
onReset={reset}
onSave={createView}
onUpdate={updateView}
/>
// Inside a DataTable the binding is automatic:
<DataTableSavedViews views={views} onSave={createView} onUpdate={updateView} />The ⋯ actions button sits next to the tablist (not inside it), so the tablist only contains tabs.
Dirty state is announced politely (“View modified.”).
Examples
Team views and permissions (CMS)
canEdit decides who may overwrite or delete; read-only views can be saved as a new personal view.
Dropdown and overflow (PRM)
variant="select" (automatic under 420px) and maxVisible tabs with the rest under More.
Bound to a Data Table
DataTableSavedViews applies the view to the table and computes dirty state with viewStateEqual.
Ridgeway Logistics | Türkiye | Lead | $31,052.00 |
|---|---|---|---|
Northwind Traders | Spain | Lead | $36,127.00 |
İstanbul Kahve Co. | Japan | Qualified | $44,518.00 |
Selvane Bio | Netherlands | Lead | $39,273.00 |
Kestrel Robotics | Germany | Proposal | $55,554.00 |
Pemberly Confectionery | Germany | Qualified | $67,674.00 |
Fernhill Media | Canada | Qualified | $33,294.00 |
Orchard Systems | United States | Lost | $84,248.00 |
1–8 of 80
API
SavedViews
| Prop | Type | Default |
|---|---|---|
views{ id, name, state: ViewState, scope: personal | team, isDefault?, icon?, ownerId?, system? }. | SavedView[] | — |
activeIdActive view; null = All (unsaved). | string | null | — |
currentThe live list configuration (from useDataTable().savedViewsBinding or your own list). | ViewState | — |
isDirtyDefault: !viewStateEqual(current, active.state) — order-insensitive, ignores clause ids and page. | boolean | — |
onSelectTabs use manual activation: arrows move focus, Enter / Space switch (switching usually fetches). | (id: string | null) => void | — |
onSaveCreate from the dialog (current state) or Duplicate (state set). Resolve with the stored view to activate it. | ({ name, scope, isDefault?, state? }) => SavedView | Promise<SavedView | void> | — |
onUpdateSave: overwrite the active view. | (id, state) => void | Promise<void> | — |
onRename / onDelete / onSetDefault⋯ menu actions. Delete confirms first; deleting the active view falls back to the default. | (id, …) => void | Promise<void> | — |
onResetDrop unsaved changes. | () => void | — |
getShareUrlCopy link. Default: the current URL with sv=<id>. | (id) => string | — |
permissionsTeam scope in the dialog; who may save, rename and delete. System views are never editable. | { canCreateTeam?, canEdit?(view) } | — |
variantauto switches to select under 420px of container width. | "auto" | "tabs" | "select" | "auto" |
showAllBuilt-in “All” entry. | boolean | true |
maxVisibleTabs before More (the active view always stays visible). | number | 4 |
sortByNameSort with the locale collator (system views first). | boolean | false |
panelIdaria-controls target (the list the tabs drive). | string | — |
sizeTab height. | "sm" | "md" | "md" |
labelsBuilt-in strings (savedViews namespace). | Partial<SavedViewsLabels> | — |
Helpers
| Prop | Type | Default |
|---|---|---|
viewStateEqual(a, b)Order-insensitive comparison used for dirty state. | boolean | — |
toViewState(listState)What a view stores (no page position; keeps page size). | ViewState | — |
applyViewState(list, view, id, base?)Apply a view and reset to the first page. | ListState | — |
resolveInitialView(views, urlId)URL sv → the default view → All. | SavedView | null | — |