Gabriel França

~ or gfrnca

ui/ux & front-end engineer. i'm basically obsessed with making things look and feel great

How nuqs transformed my state management flow in Next.js

When I started building interfaces with Next.js, I kept hitting the same pain point: synchronizing filters, searches, and pagination between components, the URL, and cached data. The user clicked, the screen changed, but the URL didn’t reflect anything — and reloading the page made everything disappear. That’s when I discovered nuqs.

What is nuqs

nuqs is a lightweight library that synchronizes interface state with the query string of the URL. It replaces that messy combination of local states and global contexts with something simple: the browser’s own address bar.

That means any change in state (filters, search, current page, theme, etc.) can be shared just by copying the URL. It’s as if the state itself becomes part of navigation.

Why it made a difference for me

I used to build complex structures with React Context, Redux, or Zustand just to store small things like “which tab is open” or “which filter is active.” The problem is that this doesn’t persist between sessions and doesn’t reflect in the URL, which is bad for UX and SEO.

With nuqs, I realized that much of this state was actually URL state — things that should be visible and reproducible. Now, every filter or page is a link. And that changes everything.

A real example

In an analytics dashboard I built, there were filters for date, company, and status. Before, each one was saved in React state and reset on reload. With nuqs, I did this:

"use client";
import { useQueryState, parseAsString } from "nuqs";

export function Filters() {
  const [company, setCompany] = useQueryState(
    "company",
    parseAsString.withDefault("all")
  );
  const [status, setStatus] = useQueryState(
    "status",
    parseAsString.withDefault("active")
  );

  return (
    <div className="flex gap-2">
      <select value={company} onChange={(e) => setCompany(e.target.value)}>
        <option value="all">All</option>
        <option value="intrabank">Intrabank</option>
        <option value="xesq">Xesq</option>
      </select>

      <select value={status} onChange={(e) => setStatus(e.target.value)}>
        <option value="active">Active</option>
        <option value="pending">Pending</option>
      </select>
    </div>
  );
}

The URL becomes:

/dashboard?company=intrabank&status=pending

That means I can send this link on Slack, and the other person will see exactly the same screen I do. No backend saving, no global state, no hacks.

Practical benefits

  • Instant state sharing
  • Persistence across reloads
  • Better browser history traceability
  • More predictable, decoupled code

Conclusion

nuqs has become a central part of my front-end workflow with Next.js. Today, whenever I see a state that affects navigation or needs to be shareable, I already know: it belongs in the URL.

More than just a library, nuqs taught me to see application state as part of navigation — and that took the clarity and simplicity of my code to another level.