Comparison to nuqs

How kvant relates to nuqs, and the key differences between them.

nuqs is the library that popularized type-safe URL query state for React: useQueryState syncs a piece of state with a search param, using parsers to convert between strings and typed values. kvant's API is heavily inspired by nuqs. If you've used it, the core loop (schema to hook to URL as source of truth) will feel familiar.

import { ,  } from 'nuqs'

const [, ] = ('page', .(0))

Key differences

Many interfaces, not only search params

nuqs syncs state with URL search params. kvant treats search params as one of many key-value interfaces, all sharing one API:

nuqskvant
Search ParamsuseQueryStateuseSearchParams / useRouteQuery (Vue Router, Nuxt)
Route ParamsuseRouteParams (Vue Router, Nuxt)
Router QueryuseRouterQuery (Next.js pages router)
Local StorageuseLocalStorage
Session StorageuseSessionStorage
CookiesuseCookies
Custom AdaptersLimited to search params, undocumentedSupported & documented

Same mental model everywhere:

import { ,  } from 'kvantjs/vue-router'
import { , ,  } from 'kvantjs/vue'
import * as  from 'kvantjs/schema'

// Route query: shareable, bookmarkable
const  = ('q', .().(''))

// Route params: identifiers that belong in the path (/users/[id])
const  = ('id', .().(1))

// localStorage: persists across reloads, syncs across tabs
const  = ('theme', .(['light', 'dark']).('light'))

// sessionStorage: scoped to the current tab
const  = ('draft', .().(''))

// Cookies: readable by the server, respect Set-Cookie attributes
const  = ('consent', .().(false), {
  : 60 * 60 * 24 * 365,
})

Full guides: Route Query · Route Params · Local Storage · Cookies

Multiple framework families

nuqs is React-only (with adapters for Next.js, React Router and other React-based frameworks). kvant additionally supports Vue, Vue Router, and Nuxt with idiomatic composables that return writable refs:

<script setup lang="ts">
import {  } from 'kvantjs/vue-router'
import * as  from 'kvantjs/schema'

const  = ('q', .().(''))
</script>

Options live at binding time, not call time

In nuqs, you can pass options per setState call (setPage(2, { history: 'push' })). kvant doesn't allow this by design: options are set when binding (hook call) or globally (options providers, defineKvantState defaults).

kvant hooks mirror the framework's original state API as closely as possible: React's setState doesn't take options, Vue's ref.value = can't take options, so kvant's setters don't either. One state API to learn, no surprises when switching interfaces.

Schemas instead of parsers

nuqs parsers define parse/serialize (with eq for comparison). kvant schemas define parse/encode and add a fluent, Zod-like API on top: checks, transforms, defaults, structures, via the built-in kvantjs/schema library. Zod and other validation libraries slot in too.

Migrating from nuqs?

The concepts map closely:

nuqskvant
useQueryState(key, parser)useSearchParams(key, schema)
useQueryStates(keyMap)useSearchParams(keyMap)
parsers (parseAsInteger, ...)schemas (kv.int(), ...)
parse / serializeparse / encode
.withDefault(value).default(value)
clearOnDefault.default(value, { clearOnDefault })

On this page