Named views

A "view" is a saved snapshot of the grid's state - sort, filters, column order/width/visibility, page, grouping. Named views let a user save the layout they like and switch between them. SvGrid ships this as a small manager over the grid's getState() / setState(), with pluggable storage.

save() captures a getState snapshot of sort, filter, columns, and page into a named view held in storage; load() reads it back and setState restores the grid to that view.

Open the live example: Named views (Rows & Cells)

<script lang="ts">
  import { SvGrid, createNamedViews, localStorageViews, type SvGridApi } from '@svgrid/grid'

  let views: ReturnType<typeof createNamedViews> | null = null

  function onApiReady(api: SvGridApi<F, Row>) {
    views = createNamedViews(api, { storage: localStorageViews('my-grid-views') })
  }
</script>

<SvGrid {data} {columns} {features} onApiReady={onApiReady} />

<button onclick={() => views?.save('Top earners')}>Save</button>
<button onclick={() => views?.load('Top earners')}>Restore</button>

The manager API

createNamedViews(api, { storage }) returns:

Method Does
list() All saved views, oldest first.
save(name) Capture the current state under name (overwrites a dup).
load(name) Apply a saved view. Returns false if unknown.
remove(name) Delete a view. Returns false if unknown.
rename(a, b) Rename, unless b already exists.
has(name) Whether a view exists.

It's pure and synchronous - all persistence goes through the storage adapter.

Storage adapters

const serverStorage: ViewStorage = {
  read: () => cachedViews,
  write: (views) => { cachedViews = views; fetch('/api/views', { method: 'PUT', body: JSON.stringify(views) }) },
}
createNamedViews(api, { storage: serverStorage })

Notes

See the live Named views demo.

Save a view, change things, switch back

api.getState() hands you the whole view - sort, filters, column widths, order, pinning, page - as one serializable object, and api.setState() puts a snapshot back. A named view is nothing more than a record of those. Sort a column, drag one wider, save it, disturb everything, then pick it from the list.

<script lang="ts">
  import { SvGrid, type GridColumns, type SvGridApi, type SvGridViewState } from '@svgrid/grid'

  type Person = { id: number; name: string; city: string; salary: number }

  const people: Person[] = [
    { id: 1, name: 'Ada Lovelace',   city: 'London',   salary: 142000 },
    { id: 2, name: 'Grace Hopper',   city: 'New York', salary: 168000 },
    { id: 3, name: 'Linus Torvalds', city: 'Portland', salary: 155000 },
    { id: 4, name: 'Radia Perlman',  city: 'Seattle',  salary: 161000 },
  ]

  const columns: GridColumns<Person> = [
    { field: 'name',   header: 'Name',   width: 180 },
    { field: 'city',   header: 'City',   width: 150 },
    { field: 'salary', header: 'Salary', width: 140,
      format: { type: 'currency', currency: 'USD' } },
  ]

  let api = $state<SvGridApi<{}, Person> | null>(null)
  let views = $state<Record<string, SvGridViewState>>({})
  let current = $state('')

  function save() {
    const snapshot = api?.getState()
    if (!snapshot) return
    const name = 'View ' + (Object.keys(views).length + 1)
    views = { ...views, [name]: snapshot }
    current = name
  }

  function restore(name: string) {
    const view = views[name]
    if (!view) return
    api?.setState(view)
    current = name
  }
</script>

<div>
  <button type="button" onclick={save}>Save this view</button>
  <select value={current} onchange={(e) => restore(e.currentTarget.value)}>
    <option value="">(pick a view)</option>
    {#each Object.keys(views) as name}
      <option value={name}>{name}</option>
    {/each}
  </select>
</div>

<SvGrid
  data={people}
  {columns}
  sortable
  filterable
  filterMode="row"

  onApiReady={(next) => (api = next)}
/>

Views survive a reload when you put them somewhere

The only difference between a view that disappears on refresh and one that does not is where the object lives. The snapshot is plain JSON, so localStorage is the cheapest place; a row keyed by user is the one you actually want once views are shared. setState takes a partial, which is why restoring an old snapshot that predates a column still works - the keys it does not carry are simply left alone.

<script lang="ts">
  import { SvGrid, type GridColumns, type SvGridApi, type SvGridViewState } from '@svgrid/grid'

  type Person = { id: number; name: string; city: string }

  const people: Person[] = [
    { id: 1, name: 'Ada Lovelace',   city: 'London' },
    { id: 2, name: 'Grace Hopper',   city: 'New York' },
    { id: 3, name: 'Linus Torvalds', city: 'Portland' },
  ]

  const KEY = 'svgrid-docs-named-view'

  const columns: GridColumns<Person> = [
    { field: 'name', header: 'Name', width: 190 },
    { field: 'city', header: 'City', width: 160 },
  ]

  let api = $state<SvGridApi<{}, Person> | null>(null)
  let status = $state('nothing saved yet')

  function save() {
    const snapshot = api?.getState()
    if (!snapshot) return
    localStorage.setItem(KEY, JSON.stringify(snapshot))
    status = 'saved (sorting: ' + JSON.stringify(snapshot.sorting) + ')'
  }

  function load() {
    const raw = localStorage.getItem(KEY)
    if (!raw) {
      status = 'nothing saved yet'
      return
    }
    // A partial is fine: only the keys present are applied.
    api?.setState(JSON.parse(raw) as Partial<SvGridViewState>)
    status = 'restored'
  }
</script>

<div>
  <button type="button" onclick={save}>Save</button>
  <button type="button" onclick={load}>Load</button>
  <span>{status}</span>
</div>

<SvGrid data={people} {columns} sortable onApiReady={(next) => (api = next)} />

Related articles