Open-source project
47ng/nuqs avatar
47ng/nuqs

nuqs: URL query string as React state, with types

Type-safe search params state manager for React frameworks - Like useState, but stored in the URL query string.

10,869 stars296 forksTypeScriptMIT

At a glance

What is it?
nuqs is a TypeScript state manager that keeps React state in the URL query string. It supports Next.js, Remix, React Router and plain SPAs through adapters, and it is MIT licensed.
Who is it for?
Adopt nuqs if your React app already treats the URL as shareable state and you want parsers and types instead of hand-rolled history calls. Skip it if your state belongs in a store or a database, or if you cannot add an adapter at the root of your component tree.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 2, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem nuqs solves for React apps

A filter panel, a pagination cursor, a selected tab. These are UI states that users expect to share, bookmark and reach with the Back button. The usual React answer is useState, which loses everything on reload, or a hand-written combination of useSearchParams, URLSearchParams and history calls, which loses type safety and grows repetitive fast.

nuqs takes the position that the URL is the source of truth. The README describes it as "Like useState, but stored in the URL query string". You call a hook, you get a value and a setter, and the query string changes underneath. The value comes back typed, because you attach a parser.

It is aimed at React developers building anything where the current view should be linkable: dashboards, search results, admin tables, docs with tabs. If your app is a single screen with no shareable state, the abstraction earns nothing.

Parsers, adapters and shallow updates: how nuqs is wired

Three mechanisms carry the design.

Parsers convert between the string in the URL and the value in your component. The README lists built-in parsers for integer, float, boolean and Date, and you can write your own for custom types and prettier URLs. This is where the type safety actually lives: the parser is the contract between the query string and your component props.

Adapters connect nuqs to a router. The README requires wrapping your component tree with an adapter, and gives one per framework: nuqs/adapters/next/app, nuqs/adapters/next/pages, nuqs/adapters/react, nuqs/adapters/remix, and nuqs/adapters/react-router/v6, v7 and v8. There is also a documented path for custom routers. Adapters are the reason the same hooks work across frameworks, and also the reason a framework upgrade can force an adapter change: the README states that Next.js support requires >=14.2.0, and that older versions should install nuqs@^1, which it says does not need the adapter code.

Shallow mode is the default for URL query updates, and the README describes notifying server components as opt-in. That default matters. Query string changes normally do not need a server round trip, so shallow updates keep interactions local. The trade-off is that server components will not see the new params unless you opt in.

Two smaller pieces round it out. useQueryStates groups related query strings into one hook, which avoids several independent hooks fighting over the same history entry. The README also documents a server cache for type-safe searchParams access in nested server components, and support for useTransition to get loading states on server updates. History behaviour is a choice: replace, or append so the Back button walks through state updates.

Installing nuqs and wiring the first hook

Install with your package manager. The README lists npm, pnpm, yarn, bun, deno and vlt, all pointing at the same package name.

bash
npm install nuqs

Before any hook works, the component tree needs an adapter. For the Next.js app router the README puts NuqsAdapter in the root layout, imported from nuqs/adapters/next/app.

tsx
// src/app/layout.tsx
import { NuqsAdapter } from 'nuqs/adapters/next/app'
import { type ReactNode } from 'react'

export default function RootLayout({ children }: { children: ReactNode }) {
  return (
    <html>
      <body>
        <NuqsAdapter>{children}</NuqsAdapter>
      </body>
    </html>
  )
}

For a plain React SPA built with Vite or create-react-app, the adapter comes from nuqs/adapters/react and wraps the root render instead.

tsx
import { NuqsAdapter } from 'nuqs/adapters/react'

createRoot(document.getElementById('root')!).render(
  <NuqsAdapter>
    <App />
  </NuqsAdapter>
)

After that, a component can read and write a param. The README does not spell out a full hook example in the excerpt available here, so the shape to expect is a hook call that takes the param name and a parser, and returns the current value plus a setter. The observable result: changing the value updates the browser URL, and pasting that URL into a new tab restores the same view.

Where nuqs is the wrong tool

The URL is a public, size-limited, string-only channel. Anything you put in it is visible in the address bar, in browser history, in server logs and in any link a user copies. Session tokens, personal data and large object graphs do not belong there. nuqs does not change that; it just makes writing to the URL pleasant, which can make over-sharing easier if you are not careful about what you parse into it.

The second constraint is the adapter. A hook that reads the query string needs a router that owns the query string. If your app has no router, or if state lives in a component tree that renders outside the adapter, the hooks have nothing to talk to. The README's own note about Next.js versions below 14.2.0 is a concrete example of this coupling: the adapter path changes with the framework, and the fallback is a different major version of nuqs.

The third is shallow mode. It is the default for a reason, but if a server component must react to a param change, you have to opt into notifying the server. That is a deliberate extra step, and forgetting it produces a component that silently shows stale data rather than an error.

nuqs compared with reading searchParams directly

The obvious alternative is the platform: useSearchParams from React Router or Next.js navigation, plus URLSearchParams, plus your own parsing. That approach has no adapter requirement beyond the router you already use, no extra dependency, and no version coupling to the library. It is a reasonable choice for one or two params where you control every read site.

What it lacks is the part nuqs was built around. Every read is a string, so every consumer re-parses and re-validates, and the parsing logic drifts between components. There is no shared notion of what a valid value looks like, no grouped update for related params, and no built-in choice between replacing and appending history entries. nuqs centralises those decisions in parsers and in useQueryStates, at the cost of a dependency and an adapter.

If your app is a Vue or Svelte project, nuqs is not the answer. The README and the package metadata describe a React library with React framework adapters. The related searches include a query about nuqs for Vue, but nothing in the repository material indicates Vue support.

Maintenance, licence and upgrade surface

The repository is not archived, and the last push was on 2026-09-21. Releases are frequent and versioned: v2.10.0 on 2026-08-20, v2.10.1 on 2026-08-25, and a v2.10.2 beta on 2026-08-28. The default branch is next, which is worth knowing before you point a fork or a CI job at it.

The licence is MIT, declared in both the repository package.json and the LICENSE file the README badge links to. MIT is permissive: you can use nuqs in commercial and closed-source products, and you must keep the copyright and licence notice with the code. That is the general shape of the licence, not legal advice for your situation.

The upgrade cost is concentrated in the adapter imports and the framework version ranges. The README ties Next.js support to >=14.2.0 and tells older projects to install nuqs@^1, which means a framework upgrade and a nuqs upgrade can be entangled. React Router adapters are split by major version (v6, v7, v8), so a router major bump means changing the import path. Budget for that, and read the release notes for the version you are moving to rather than assuming the adapter path is stable.

Editorial conclusion

Adopt nuqs if your React app already treats the URL as shareable state and you want parsers and types instead of hand-rolled history calls. Skip it if your state belongs in a store or a database, or if you cannot add an adapter at the root of your component tree. Before committing, check that your framework version is covered by the adapter list in the README, and confirm whether shallow updates are enough or whether you need server components to re-render.

Frequently asked questions

What is nuqs?

nuqs is a type-safe search params state manager for React frameworks, described in the README as being like useState but stored in the URL query string. It ships parsers for common types and adapters for Next.js, plain React, Remix, React Router and custom routers.

How do I install nuqs?

Install the package with npm install nuqs, or the equivalent command for pnpm, yarn, bun, deno or vlt as listed in the README. You then wrap your component tree with the adapter for your framework, for example NuqsAdapter from nuqs/adapters/next/app in the Next.js app router root layout.

Does nuqs work with React Router?

Yes. The README documents separate adapters for React Router v6, v7 and v8, imported from nuqs/adapters/react-router/v6, /v7 and /v8 respectively. The adapter is chosen by your router's major version, so a router major upgrade means changing that import path.

Does a nuqs update notify server components?

Not by default. The README states that shallow mode is the default for URL query updates, and that notifying server components is opt-in. If a server component needs to react to a param change, you have to enable that behaviour explicitly.

What licence does nuqs use?

MIT. The repository package.json declares the MIT licence and the README badge links to the LICENSE file. That permits commercial and closed-source use provided the copyright and licence notice are retained.

Official sources

  1. 47ng/nuqs on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/47ng-nuqs.svg)](https://hysenlabs.com/projects/47ng-nuqs)