pgTable/Concepts/Persistence
Concepts~5 min

Persistence

One persist option on usePgTable. Four modes, schema versioning, optional TTL, restore on mount. Drafts and dashboards survive refresh without a separate layer — and the code that reads the table doesn't change one line.

Four modes

Persistence is a property of the table, not a plugin or a middleware. You pick a mode at mount time and the store does the rest. The four modes form a spectrum: nothing, memory-only, per-tab, per-browser.

TScharacters-table.tsx
// 'none'    — no persistence, entry cleared on unmount. (default)
// 'memory'  — entry survives unmount, no webstorage write.
// 'session' — sessionStorage write-through on every data / state change.
// 'local'   — localStorage   write-through on every data / state change.

usePgTable(charactersColumns, charactersState, {
pgTableId: 'docs:concepts:characters',
persist: { mode: 'local', schemaVersion: 1 },
})

Mount, write, unmount

The lifecycle is deliberately small. There's no separate restore step in your code, no useEffect that hydrates from storage, no manual flush.

TSlifecycle.ts
// 1. usePgTable mounts with a stable, caller-assigned pgTableId.
// 2. If mode is 'session' or 'local' and autoRestore is true (the default),
//    pgTable reads the persisted payload and applies it before returning.
// 3. Every committed setData / setState writes the new payload back synchronously.
// 4. On unmount:
//      - 'none'                       → store entry is dropped.
//      - 'memory' / 'session' / 'local' → store entry stays; reach it by id from
//                                         anywhere until you removeTable().

Schema versioning

Persisted payloads carry the schemaVersion they were written under. On restore, pgTable compares the persisted version against the current one and — if they differ — calls onPersistSchemaMismatch so you can migrate or discard. With no handler, mismatched payloads are dropped (the safe default).

TSmigration.ts
usePgTable(charactersColumns, charactersState, {
pgTableId: 'docs:concepts:characters',
persist: {
  mode: 'local',
  schemaVersion: 3,                                    // bump when shape changes
  onPersistSchemaMismatch: ({ persistedVersion, persistedPayload }) => {
    if (persistedVersion === 2) {
      // Migrate v2 → v3 in place.
      const next = migrateV2toV3(persistedPayload)
      return { shouldRestore: true, payload: next }
    }
    // Default: drop the old payload, start fresh.
    return { shouldRestore: false }
  },
},
})

Expiry

Pass expireAfter (ms) and the payload carries an expiresAt timestamp. On restore, expired payloads are deleted before they reach your subscribers — no migration handler, no warning. Use it for caches that shouldn't outlive a session of work.

TSexpiry.ts
usePgTable(charactersColumns, charactersState, {
pgTableId: 'docs:concepts:characters',
persist: {
  mode: 'local',
  expireAfter: 1000 * 60 * 60 * 24 * 7,                // 7 days
},
})

Inspect and clear

pgTable doesn't lock you out of the webstorage entries it owns. getPersistedTableProps returns a list of every persisted table in the current browser with its mode, schema version, and last-updated time. clearStoredTables wipes them (all, by id, or by mode). dropTableEverywhere is the all-in-one: in-memory entry plus webstorage entry, gone.

TScleanup.ts
import { clearStoredTables, dropTableEverywhere, getPersistedTableProps } from '@westopp/pgtable'

// Inspect every persisted table in the current browser.
getPersistedTableProps()                                 // [{ pgTableId, mode, ... }, ...]

// Wipe a specific table from both memory and webstorage.
dropTableEverywhere('docs:concepts:characters')

// Clear all persisted tables from one storage (e.g. on sign-out).
clearStoredTables({ mode: 'local' })