Custom Adapters
Binding kvant to your own key-value interfaces in React.
When the built-in interfaces aren't enough (an external store, an in-memory map, a query-string dialect, a native bridge), you can write your own adapter for any key-value interface.
The adapter contract
An adapter is a hook receiving the bound keys and resolved options, and returning an interface object:
interface KvantReactAdapterInterface<T> {
/** Unique adapter identifier, used to namespace sync channels. */
readonly key: string
/** Current raw values for the adapter's keys. Must be referentially stable between changes. */
readonly snapshot: Readonly<Record<string, T | undefined>>
/** Writes raw values to the underlying storage. */
readonly update: (values: Record<string, unknown>) => void
}
type KvantReactAdapter<T, Options> = (
keys: string[],
options: Partial<Options>,
) => KvantReactAdapterInterface<T>To have a better idea of how to implement an adapter, check out the source code of the built-in adapters:
| Interface | Adapter | Source |
|---|---|---|
| Next.js (app router) → Search Params | kvantjs/next → useSearchParamsKvantAdapter | GitHub |
| Next.js (pages router) → Router Query | kvantjs/next/pages → useRouterQueryKvantAdapter | GitHub |
| React Router → Search Params | kvantjs/react-router → useSearchParamsKvantAdapter | GitHub |
Framework-agnostic adapters
If you want to share an adapter across framework families (React, Vue), you can implement a low-level, framework-agnostic adapter:
interface KvantAdapterInterface<T> {
/** Unique adapter identifier, used to namespace sync channels. */
readonly key: string
/**
* Subscribes to snapshot changes.
* @returns An unsubscribe function.
*/
readonly subscribe: (callback: () => void) => () => void
/** Returns the current raw values for the adapter's keys. */
readonly getSnapshot: () => Record<string, T | undefined>
/**
* Returns the raw values as rendered on the server (SSR).
* Used as the server snapshot during hydration,
* the real client values are applied right after hydration.
*
* @default getSnapshot
*/
readonly getServerSnapshot?: () => Record<string, T | undefined>
/** Writes raw values to the underlying storage. */
readonly update: (values: Record<string, unknown>) => void
/**
* Register necessary effects (listeners, subscriptions, etc.)
* @returns A cleanup function to dispose of the effects.
*/
readonly effects?: () => (() => void) | void
}
type KvantAdapter<T, Options> = (
keys: string[],
options: Partial<Options>,
) => KvantAdapterInterface<T>To have a better idea of how to implement a framework-agnostic adapter, check out the source code of the built-in adapters: