Skip to main content
A collection is a named set of records that CL Sync stores, indexes, and exposes through reactive hooks. You define collections using defineCollection, which is a typed identity helper — it returns the definition object unchanged while giving TypeScript full visibility into your record shape and query arguments.

Defining a collection

The first type parameter is your record type (must extend SyncRecord). The second is the shape of query arguments, used to type deriveKey and the args parameter on useSyncCollection.

Collection options

string
required
A unique identifier for the collection. This string is used as the IndexedDB object-store key prefix and must be stable across deployments.
(record: TRecord) => SyncId
Extracts the record’s unique ID. Defaults to record._id ?? record.id. Override this when your records use a different ID field.
boolean
Whether to write records to IndexedDB. Defaults to true. Set to false for transient UI state that should not survive a page reload.
number
Milliseconds before a loaded collection slice is considered stale. After this threshold, CollectionState.staleAt is set, signalling that a fresh server fetch is worthwhile.
(record: TRecord) => TRecord | null
Called for every record before it is written to IndexedDB. Return a sanitized copy to strip sensitive fields. Return null to skip persistence for that record entirely.
(a: TRecord, b: TRecord) => number
A comparator applied when reading records from the store. Records are sorted in memory — you don’t need to sort inside your components.
(args: TArgs) => string
Converts query arguments into a stable string cache key. When args differ (e.g., different orgId values), results are stored under separate keys. If omitted, all records share a single cache key.

Examples

Basic collection

The simplest collection uses all defaults — records are persisted, ID comes from record._id ?? record.id, and there is no argument-based cache partitioning.

Partitioned collection with sort

Use deriveKey when your queries are scoped by a parent resource. Records for different orgId values will never mix in the cache.

Redacting sensitive fields before persistence

Use redactBeforePersist when you want the record in memory but don’t want sensitive values written to disk.
Returning null from redactBeforePersist skips the record entirely — the in-memory cache still holds it, but it won’t appear in IndexedDB after a reload.

Non-persisted (in-memory only) collection

Non-persisted collections are cleared when the page reloads. They behave identically to persisted collections in every other way, including subscriptions and hooks.

Reading a collection

Call store.getCollection (or use useSyncCollection in React) with the definition and any query arguments:
The distinction between undefined and [] is intentional. undefined means the store has never received data for this cache key — you should trigger a fetch. [] means the server confirmed there are no records.

Collection state

Each loaded slice also has associated CollectionState metadata:
Use staleAt to decide whether to re-fetch even when records are present.