Universal, type-safe state manager for key‑value interfaces:
URL search params, cookies, localStorage, sessionStorage,
and more.
Install kvant with your package manager of choice:
npm install kvantjspnpm add kvantjsyarn add kvantjsbun add kvantjsRead the complete documentation for your framework of choice:
Tip
Below you'll find a quick start guide based on Next.js (app router).
For complete docs on Next.js and other frameworks, please see the options above.
kvant turns key-value interfaces into React state. Bind a key, pass a
schema from kvantjs/schema, and read/write it like useState.
The interface (here, the URL) stays the single source of truth:
import { useSearchParams } from 'kvantjs/next'
import * as kv from 'kvantjs/schema'
function SearchInput() {
const [query, setQuery] = useSearchParams('q', kv.string().default(''))
return (
<input
value={query}
onChange={e => setQuery(e.target.value)}
/>
)
}?q=hello in the URL means query is 'hello'. Calling setQuery writes back to the URL.
Bind a whole key map in one call. Updates batch into a single write:
const [filters, setFilters] = useSearchParams({
q: kv.string().default(''),
page: kv.index().default(0),
sort: kv.enum(['asc', 'desc']).default('asc'),
tags: kv.array(kv.string()).default([]), // repeated params: ?tags=a&tags=b
})
setFilters(prev => ({ ...prev, page: prev.page + 1 }))Pass options as the last argument:
const [query, setQuery] = useSearchParams('q', kv.string().default(''), {
history: 'push', // add browser history entries
shallow: false, // go through the Next.js router (re-run server components)
})Set options once for a component subtree using the options provider:
import { SearchParamsOptionsProvider } from 'kvantjs/next'
<SearchParamsOptionsProvider defaultOptions={{ history: 'push' }}>
{children}
</SearchParamsOptionsProvider>The same pattern works for all supported key-value interfaces:
import { useSearchParams } from 'kvantjs/next'
import { useCookies, useLocalStorage, useSessionStorage } from 'kvantjs/react'
import * as kv from 'kvantjs/schema'
// URL search params: shareable, bookmarkable
const [query, setQuery] = useSearchParams('q', kv.string().default(''))
// localStorage: persists across reloads, syncs across tabs
const [theme, setTheme] = useLocalStorage('theme', kv.enum(['light', 'dark']).default('light'))
// sessionStorage: scoped to the current tab
const [draft, setDraft] = useSessionStorage('draft', kv.string().default(''))
// Cookies: readable by the server, respect Set-Cookie attributes
// (requires additional setup for SSR, see the full guide)
const [consent, setConsent] = useCookies(
'consent',
kv.stringbool().default(false),
{ maxAge: 60 * 60 * 24 * 365 },
)Full guides: Search Params · Local Storage · Cookies
- nuqs 🖤 played a marginal role in inspiring the kvant API, as well as providing bits and pieces of code for the kvant internals.
- kvant API is shaped around zod 💙 as
kvantjs/schemabuilds on your existing Zod intuition, so defining schemas feels just like writing plain Zod.
MIT License © Oleg Kapranov