STOP FUCKING AROUND WITH URL STATE
Just fucking use nuqs.
You've been manually parsing query strings, fighting with router state, and writing the same fucking boilerplate for years. That ends today.
Get startedJust fucking use nuqs.
Your URL state is a fucking disaster.
You've been building web apps for how long now? And you're still manually parsing window.location.search, fighting with URLSearchParams, or losing state on every fucking page refresh?
// This is what you're doing right now, isn't it?
const searchParams = new URLSearchParams(window.location.search)
const page = parseInt(searchParams.get('page') || '1')
const query = searchParams.get('q') || ''
const sortBy = searchParams.get('sort') || 'date'
const isAscending = searchParams.get('asc') === 'true'
// And when you need to update it... oh boy
const updateUrl = (key: string, value: string) => {
const url = new URL(window.location.href)
url.searchParams.set(key, value)
// Do I push? Replace? What about history?
// What about SSR? What about hydration?
// What about type safety? LOL WHAT TYPE SAFETY
window.history.pushState(null, '', url)
// Oh wait, React didn't re-render. FUCK.
}You've got 15 different ways to parse query strings across your codebase. Half of them don't handle edge cases. The other half are copy-pasted from Stack Overflow circa 2016.
This is the hell you chose. And for what? “I don't need a library for this”? Give me a fucking break.
Just fucking use nuqs.
What the fuck is nuqs?
nuqs is a type-safe search params state manager for React. Instead of fucking around with manual URL parsing, you use it like useState, except the state lives in the URL.
Want a counter that survives page refresh? Here's all you fucking need:
import { useQueryState, parseAsInteger } from 'nuqs'
function Counter() {
const [count, setCount] = useQueryState(
'count',
parseAsInteger.withDefault(0)
)
return (
<button onClick={() => setCount(c => c + 1)}>
Count: {count}
</button>
)
}No manual parsing. No type coercion bullshit. No fucking around withrouter.push. It just works.
Just fucking use nuqs.
Why it's fucking great
Type-safety, do you speak it?
Every parser knows its type. parseAsInteger gives you a number. parseAsBoolean gives you a boolean. TypeScript infers everything. No moreas any lying to TypeScript bullshit.
const [page, setPage] = useQueryState('page', parseAsInteger)
// ^? number | null
const [page, setPage] = useQueryState('page', parseAsInteger.withDefault(1))
// ^? number (never null, because you have a fucking default)Shareable fucking state
Copy the URL. Send it to someone. They get the exact same fucking state. Filters, pagination, search queries, tabs, everything. No more “can you send me a screenshot of what you're seeing?”
Back button actually works
Because state is in the URL, browser history just fucking works. Users can navigate back and forth. Bookmarks work. Deep links work. It's how the web has been working since the 1990's.
SSR & Static Export
Works with Next.js App Router, Pages Router, Remix, React Router, you name it. Server-side rendering? ✓ Static export? ✓ This very page is static and it uses nuqs. Look at the fucking URL.
Built-in parsers for everything
Strings, integers, floats, booleans, dates, arrays, JSON, enums, literals... Need something custom? Make your own fucking parser in 5 lines.
import {
parseAsString,
parseAsInteger,
parseAsFloat,
parseAsBoolean,
parseAsIsoDateTime,
parseAsArrayOf,
parseAsJson,
parseAsStringEnum,
} from 'nuqs'
// All type-safe. All fucking work.Just fucking use nuqs.
See it in fucking action
Play with these demos. Watch the URL change. Refresh the page. Copy the URL and open it in a new tab. It all just fucking works.
Just fucking use nuqs.
“But wait...”
“I can just use URLSearchParams!”
Sure, you can also write your own HTTP client instead of using fetch. You can do a lot of things. Doesn't mean you fucking should. URLSearchParams doesn't give you type safety, doesn't integrate with React state, doesn't handle SSR, and doesn't respect your fucking time.
“It's another dependency!”
It's 6KB gzipped. You're already shipping 400KB of fucking JavaScript. This isn't the dependency to worry about: it's the one that'll save you from maintaining 400 lines of URL parsing bullshit.
“Not everything should be in the URL!”
Indeed. And nuqs doesn't force you to put everything there. Use it for what makes fucking sense: filters, pagination, search queries, active tabs, modal states. The stuff users expect to be shareable and bookmarkable. Keep your ephemeral UI state in regular useState.
“What about sensitive data?”
Don't put fucking passwords in the URL. This is basic web security. nuqs is for shareable state, not secrets.
“What about security? XSS?”
Don't eval the fucking URL. Parsers can keep your app safe, but don't fuck around in there. If you're doing a ?redirect=<url>, make sure your parser handles it (eg: with startsWith('/')) to avoid open redirects.
I use TanStack Router/Start, do I need nuqs?
Need? No. TanStack Router has built-in support for type-safe URL state management. It's fucking awesome. If you're already using it, use its APIs, you're going to have a fucking great DX.
But nuqs is compatible with it so you can use components from 3rd party sources that rely on nuqs in your TanStack app.
Just fucking use nuqs.
When should you use nuqs?
- •Building filters or search? Use nuqs.
- •Need pagination? Use nuqs.
- •Tab navigation? Use nuqs.
- •Modal or drawer state? Use nuqs.
- •User preferences that should be shareable? Use nuqs.
- •Any public state that should survive page refresh? Use nuqs.
- •Want to stop fucking around? Use nuqs.
Just fucking use nuqs.
Stop overthinking. Start building.
nuqs isn't perfect. Nothing is. But it solves real problems that URL state management has had for years. So stop reading articles, stop watching comparison videos, stop asking Twitter.
Just fucking use nuqs.
pnpm add nuqs