@victorylabs/params
v0.6.0
Published
Centralized view-state config (filters, sort, pagination, app config) with pluggable storage. Memory by default; URL/localStorage/sessionStorage opt-in. Single React hook DX, schema-validated reads, cross-component sharing.
Downloads
299
Readme
@victorylabs/params
Centralized view-state config (filters, sort, pagination, app config) with pluggable storage. Memory by default; URL/localStorage/sessionStorage opt-in. Single React hook DX, schema-validated reads, cross-component sharing.
A focused alternative to nuqs / hand-rolled useSearchParams + useEffect chains, with:
- Single hook —
useParams(def)returns a controller; everything else flows fromp.. - Pluggable storage — memory (default), URL, localStorage, sessionStorage, or your own backend implementing
ParamsStorage<T>. - Schema-validated reads — Zod / Standard Schema / plain spec; corrupted external data silently falls back to defaults.
- Cross-component sharing — multiple
useParams(def)calls in different components share the same store automatically (definition identity). - Per-field debounce + input shadow — typing feels instant; URL writes are throttled.
- Compile-time path safety —
p.value('emial')is a TypeScript error. - Per-field URL codecs — a field's
parse/serializeowns its whole wire form; arrays default to CSV (?tags=a,b), tunable viaurlStorage({ arrayFormat }). - Deterministic
p.toQuery()/p.href()— alphabetical and matchingp.set(...)exactly (same keys + encoding); suitable for React Query cache keys and deep links. - Forms bridge —
paramsToFormSync(def)lets@victorylabs/formsborrow a params definition (one source of truth for filters + a save-view form).
Install
pnpm add @victorylabs/params zodreact, zod, and @victorylabs/forms are optional peer dependencies. Install only what you use.
Quick start
import { defineParams } from '@victorylabs/params'
import { useParams } from '@victorylabs/params/react'
import { urlStorage } from '@victorylabs/params/storage/url'
import { z } from 'zod'
const filters = defineParams(
{
query: z.string().default(''),
page: z.coerce.number().int().min(1).default(1),
sort: z.enum(['asc', 'desc']).default('asc'),
},
{
storage: urlStorage(),
fields: {
query: { debounce: 300 },
page: { omitWhenDefault: true },
},
},
)
export function ProductsPage() {
const p = useParams(filters)
return (
<>
<input {...p.input('query')} placeholder="Search…" />
<select value={p.values.sort} onChange={(e) => p.set('sort', e.target.value as 'asc' | 'desc')}>
<option value="asc">A → Z</option>
<option value="desc">Z → A</option>
</select>
<ResultList query={p.deferred('query')} sort={p.value('sort')} page={p.value('page')} />
<a href={p.href({ page: p.values.page + 1 })}>Next</a>
</>
)
}Documentation
Full reference at the docs site.
License
MIT
