feat: Key isolation
Key isolation (also known as "fine grained subscriptions") is the fact that updating a search param only re-renders hooks subscribed to this key.
Example:
-
<ComponentA>has auseQueryState('a') -
<ComponentB>has auseQueryState('b')
Updating the state for a doesn't re-render ComponentB.
Most frameworks (Next.js & React Router) re-render every call site of their useSearchParams hooks when any search params change (via navigation events), causing unnecessary re-renders of useQueryState(s). Some other routers like TanStack Router do implement key isolation.
Since in the React SPA adapter we control the whole thing (there is no router so to speak), we can implement caching to detect only relevant differences and avoid re-rendering unnecessarily.
For React Router & Remix, we can do this too in shallow mode, since their useSearchParams doesn't re-render on shallow updates. When using shallow: false, everything would re-render after the URL has updated (when coming back from the loader). This feels like a natural behaviour that is in line with the frameworks navigation.
For Next.js, this might require closer collaboration with the core team to implement this filtering built-in, because their useSearchParams does re-render on any search param change, even when done in a shallow manner. This is because it uses a single Context that is reactive to shallow updates (so we can't do the same trick as in the other routers):
- Value: https://github.com/vercel/next.js/blob/90164a10017c7a7f42d26c7ac36c4cc1db258a6c/packages/next/src/client/components/app-router.tsx#L255-L268
- Provider: https://github.com/vercel/next.js/blob/90164a10017c7a7f42d26c7ac36c4cc1db258a6c/packages/next/src/client/components/app-router.tsx#L645
- Consumer: https://github.com/vercel/next.js/blob/90164a10017c7a7f42d26c7ac36c4cc1db258a6c/packages/next/src/client/components/navigation.ts#L42
The latest updates on your projects. Learn more about Vercel for GitHub.
| Project | Deployment | Preview | Comments | Updated (UTC) |
|---|---|---|---|---|
| nuqs | Preview | Comment | Aug 18, 2025 11:36am |
:tada: This PR is included in version 2.5.0-beta.6 :tada:
The release is available on:
Your semantic-release bot :package::rocket:
:tada: This PR is included in version 2.5.0 :tada:
The release is available on:
Your semantic-release bot :package::rocket:
@franky47 are there any solutions you can think of that we can implement currently that would prevent excessive re-renders? we're currently use deckgl to lift state from the url but its causing multiple rerenders which results in unexpected layer rendering
The React Compiler is one that comes to mind, it would memoise components so they don't rerender if their props are the same. It may require you to slice your components a little differently, with a parent component doing the URL state access, and feeding that (or derived props) to heavy children components that'd be target for auto-memoisation.
You could also do the same thing the compiler does, with React.memo, but it's a lot more work and can be brittle, more here:
http://podrocket.logrocket.com/the-useless-usecallback-react-performance-myths-dominik-dorfmeister