Skip to content
AdaptTable

Vue saved views and table preferences

Capture named Vue table views, persist column layout explicitly, and compose density and fullscreen controls.

These examples use @adapttable/vue-unstyled. Import matching feature paths from your selected adapter so its own kit renders the controls. See Vue installation and providers.

import { densityChooser } from "@adapttable/vue-unstyled/density";
import { fullscreen } from "@adapttable/vue-unstyled/fullscreen";
import { savedViews } from "@adapttable/vue-unstyled/saved-views";
const features = [
densityChooser(),
fullscreen(),
savedViews({ storageKey: "people-views" }),
];

densityChooser() and fullscreen() return static features. density: TableDensity is "comfortable" | "compact". v-model:density accepts update:density; a host that ignores a controlled request keeps its value, and the native select restores that value. onDensityChange is an optional observer called once for the request; defaultDensity seeds local state. The root exposes effective data-density.

useFullscreen(target, activity?) from the binding root returns a computed FullscreenState. The target accepts an element, ref or getter returning HTMLElement | null | undefined. Browser listeners activate after mount. Unsupported environments have no fullscreen control. The active container is passed to kit overlays so filters can remain inside the fullscreen surface. Fullscreen itself is browser state and is not saved in a view.

savedViews(options: MaybeRefOrGetter<UseSavedViewsOptions>) installs one scope-owned model and native menu. Options require storageKey; optional storage (null for memory only), store: SavedViewsStore, visibility, migrate, urlAdapter, urlSync and urlKey configure persistence and the captured table namespace. An explicit store replaces browser storage. flushViewState?: () => void lets the host flush pending URL slices immediately before saving or applying a view; the installed feature also flushes its own table-owned writers.

useSavedViews(options, activity?): UseSavedViewsResult returns computed views and defaultView, plus save(name), apply(name), remove(name), rename(from, to), move(name, -1 | 1), setDefault(name) and reload(). SavedView, SavedViewVisibility, SavedViewMigration, SavedViewsStore and SAVED_VIEW_VERSION describe the shared persisted format and migration contract. Saving/applying through the installed feature flushes pending feature state writes first.

SavedViewsPanel from the native entry takes SavedViewsPanelProps: views, onApply, onRename, onMove, onSetDefault, onRemove, optional labels, footer and class name. Connect these to useSavedViews for a separate management surface. Adapter authors can render SavedViewsPanelChrome from @adapttable/vue/adapter with SavedViewsPanelChromeProps, providing the required SavedViewsPanelSlots controls. It coordinates row actions and rename state while the supplied controls determine the appearance.

Saved Views capture recognized URL parameters from their configured namespace. For this Vue slice, the connected source can serialize pagination, search, sort levels, filter extras/tree, grouping keys and group aggregate overrides. Density participates when densityChooser() is installed. Uncontrolled data pins participate with rowPinning(). Group collapse participates only when explicitly wired to useGroupCollapseUrlState:

import {
grouping,
useGroupCollapseUrlState,
} from "@adapttable/vue-unstyled/grouping";
const collapse = useGroupCollapseUrlState({ urlKey: "people" });
const features = [
grouping("team", {
collapsedGroupIds: collapse.collapsedGroupIds,
onCollapsedGroupIdsChange: collapse.onCollapsedGroupIdsChange,
}),
];
// Use urlKey="people" on the table and its Saved Views configuration too.

UseGroupCollapseUrlStateOptions extends UrlSliceOptions with optional reactive default IDs; UseGroupCollapseUrlStateResult is the ref/change pair above. useDensityUrlState(options?, activity?) accepts UseDensityUrlStateOptions, returning UseDensityUrlStateResult with a density ref, onDensityChange and flush. Density aliases TableDensity; DENSITY_URL_WRITE_DEBOUNCE_MS exposes the shared write delay. useRowPinningUrlState(options?) accepts UseRowPinningUrlStateOptions (enabled, adapter, sync and key) and returns UseRowPinningUrlStateResult with pin state and its writer.

useUrlSlice<T, TConfig>(options, spec, config, activity?) is the generic bridge: a readonly shallow value ref, set(next) and flush(). UrlSliceOptions holds reactive adapter/key/sync and optional serverSearch for hydration. Use the same adapter and key for related slices. If a table uses a separately supplied source, align that source’s URL backend/namespace with the table and view controls, especially when URL sync is disabled and memory adapters would otherwise be separate.

Column layout participates when the host explicitly binds useColumnLayoutUrlState as shown below. Tree/detail expansion, selected IDs, edit drafts/history, host rows, spans, extra content and appearance callbacks are not captured. Controlled state remains authoritative: a saved URL value cannot override an unchanged controlled prop. The shared codec recognizing additional features’ parameters does not install those features or make their state reactive in Vue.

The binding root exports both layout-persistence composables. Neither is installed automatically by DataTable.

<script setup lang="ts">
import { useColumnLayoutUrlState } from "@adapttable/vue";
import { DataTable, type ColumnDef } from "@adapttable/vue-unstyled";
import { resizableColumns } from "@adapttable/vue-unstyled/resizable-columns";
import { savedViews } from "@adapttable/vue-unstyled/saved-views";
interface Person {
id: string;
name: string;
score: number;
}
const rows: Person[] = [{ id: "ada", name: "Ada", score: 10 }];
const columns: ColumnDef<Person>[] = [{ key: "name" }, { key: "score" }];
const rowKey = (person: Person) => person.id;
const { layout, onLayoutChange, flush } = useColumnLayoutUrlState({
urlKey: "people",
});
const features = [
resizableColumns(),
savedViews({ storageKey: "people-views", flushViewState: flush }),
];
</script>
<template>
<DataTable
:data="rows"
:columns="columns"
:row-key="rowKey"
:features="features"
:column-layout="layout"
url-key="people"
@update:column-layout="onLayoutChange"
/>
</template>

useColumnLayoutUrlState(options?, activity?): UseColumnLayoutUrlStateResult accepts MaybeRefOrGetter<UseColumnLayoutUrlStateOptions>. The options extend UrlSliceOptions with reactive optional defaultColumnLayout: Partial<ColumnLayoutState>. The result contains a readonly shallow layout ref, onLayoutChange(next: ColumnLayoutState) and flush(). Persisted fields include hidden/order/widths/pinned/names and collapsed column groups. LAYOUT_URL_WRITE_DEBOUNCE_MS exposes the shared write delay; passing flush to Saved Views captures the latest resize and prevents an older pending write from overwriting an applied view.

Use the same urlAdapter and urlKey on the layout composable, source/table and Saved Views. For urlSync: false, provide a shared explicit memory adapter when independently created components need the same state. To reject a layout request, inspect the event before calling onLayoutChange; the table keeps the supplied layout until the host accepts it. During SSR, seed a request-local adapter and use the same initial state on the client.

For a browser preference that does not join URL/Saved Views, use:

import { useColumnLayoutStorageState } from "@adapttable/vue";
const { layout, onLayoutChange } = useColumnLayoutStorageState({
storageKey: "people-column-layout",
defaultColumnLayout: { hidden: ["score"] },
});
// Bind the same column-layout prop and update:column-layout event as above.

UseColumnLayoutStorageStateOptions requires reactive storageKey and accepts reactive optional storage: LayoutStorage and defaultColumnLayout. useColumnLayoutStorageState(options, activity?) returns UseColumnLayoutStorageStateResult: a readonly shallow layout ref and onLayoutChange. Default browser storage is discovered and read only after mount; unavailable storage leaves an in-memory preference. SSR and the first hydration render use the fallback layout. Changing storage/key switches the destination; returning to the default removes its saved entry. This controller does not automatically merge a storage preference with a URL layout. Choose one authoritative layout owner, or define that precedence in the host.

ViewControlPresentation contains resolved labels, direction, optional class names and container. DensityControlProps, FullscreenControlProps and SavedViewsControlProps add the corresponding state/actions. DENSITY_CONTROL, FULLSCREEN_CONTROL and SAVED_VIEWS_CONTROL are required feature slot keys. FULLSCREEN_MODEL and SAVED_VIEWS_MODEL publish their models in the table’s state registry.

DensityChooserChrome(props) requires DensityChooserSlots.Control, receiving attrs, current value, localized options and onChange. FullscreenButtonChrome(props) requires Button, receiving ViewControlButtonProps with semantic attrs and label. SavedViewsMenuChrome(props: SavedViewsMenuChromeProps) requires SavedViewsMenuSlots: Trigger, Button, Input and Panel. It owns disclosure, keyboard and focus state; the kit draws each control/surface.

Use the Vue live demo to switch adapters and the Vue Feature Lab to compose features. The feature index lists native imports and API reference documents the binding and controlled contracts.

Third-party notices