
Playing with Search Params using nuqs in Next.js App Router
M. Zakyuddin Munziri
@zakiego
Originally written in Bahasa Indonesia.
Abstract
nuqs is a search params state manager library for Next.js.
The default behavior of nuqs is client-first, meaning when updating search params, the web page won't re-render. This is great if the search params value only needs to be read on the client side.
However, if the value needs to be read by the server, changes won't be detected. To trigger a re-render so the server can read the changes, you need to add the { shallow: false } option.
This differs from next-query-params which adopts a server-first approach, meaning every value change is immediately read by the server.
Experience the difference between client-side and server-side search params in
nuqsat nuqs-playground.vercel.appFor
next-query-params, you can try it at seach-params-playground.vercel.app
Introduction
This article is a continuation of Creating a Search Component in Next.js App Router. Previously, we used the next-query-params library to manage search params. This time, we'll play with nuqs.
Actually, I had tried nuqs before. But due to limited knowledge, I didn't continue with it.
Thanks to @alfonsusac for triggering me to read the nuqs documentation more deeply.
Overview
nuqs defines itself as "Type-safe search params state manager for Next.js".
Let's begin. Of course, the framework used in this article is Next.js.
First, install nuqs.
pnpm add nuqs
Create a component file containing an <input/>, then forward the onChange value to search params using useQueryState.
// src/app/client.tsx
'use client'
import { useQueryState } from 'nuqs'
export function Demo() {
const [name, setName] = useQueryState("name");
return (
<>
<input value={name || ""} onChange={(e) => setName(e.target.value)} />
<p>Hello, {name}!</p>
</>
);
}
Why do we need to write "use client" at the very top?
Because since the introduction of the App Router paradigm in Next.js, every component is a server component by default. However, in this case, we need the
onChangefrom<input/>which is a client component. Therefore, we need to write "use client" at the top line to tell React that this is a client component (Client Components).
Finally, import the <Demo/> component we created into /src/app/hello/page.tsx.
The result is that every time you enter a value in <input/>, the search params will be updated immediately.
For example, example.com/hello will change to example.com/hello?name=zaki.
For more examples, check out nuqs.47ng.com/playground.
Difference between nuqs and next-query-params
Previously, I used nuqs in this tweet. The most noticeable difference between nuqs and next-query-params is:
nuqsruns on client-first- while
next-query-paramsis server-first
What's the difference?
You can directly experience the difference between client-first (nuqs-playground.vercel.app/nuqs-client) and server-first (nuqs-playground.vercel.app/nuqs-server)
With nuqs being client-first, we cannot access search params from the server side.
For example, we visit the page example.com/hello.
Then, when typing a name in the input, the URL changes to example.com/hello?name=zaki.
Although the URL changes, because nuqs is client-first, the value of name we get from search params will remain empty, if using server side.
nuqs by default doesn't trigger a page update to the server. So even though the URL in our browser changes to example.com/hello?name=zaki, the server still considers us to be on the example.com/hello page only.
interface Props {
searchParams: {
name?: string;
};
}
export default async function Page(props: Props) {
const { searchParams } = props;
const { name } = searchParams;
return (
<div>
value from server: {name}
</div>
);
}
However, it's different in client components, like in the <Demo/> earlier.
Let me place the code once more.
// src/app/client.tsx
'use client'
import { useQueryState } from 'nuqs'
export function Demo() {
const [name, setName] = useQueryState("name");
return (
<>
<input value={name || ""} onChange={(e) => setName(e.target.value)} />
<p>Hello, {name}!</p>
</>
);
}
In that code, the value of name will be read immediately, even though we don't update the page to the server.
This is the opposite of next-query-params, where every time you update the value in <input/>, it will trigger a page update.
As a result, when the URL updates from example.com/hello to example.com/hello?name=zaki, the server immediately recognizes that we're already at the URL example.com/hello?name=zaki.
nuqs but Server Side
After reading more carefully, although nuqs is client-first, we can still run a re-render by adding { shallow: false }.
Docs nuqs.47ng.com/docs/options
useQueryState("foo", { shallow: false });
With the above setting, we can get search params through the server side.
You can try it here nuqs-playground.vercel.app/nuqs-server.
Bonus
Here are some replies from the nuqs author directly:
Also, it turns out nuqs is used by vercel.com too
Conclusion
nuqs has a better DX (developer experience) compared to next-query-params, because you don't need to add code to the layout feature.
However, because nuqs is client-first, the search params value changes cannot be obtained directly on the server side, unless using { shallow: false }. This differs from next-query-params which is server-first from the start, so value changes can be read by the server immediately.
In the end, it all comes back to the developer. The questions are:
- Do you fetch on the server-side or client side?
- Next, does the search params value need to be readable by the server? Because in some cases, search params don't have to be immediately readable by the server
That's all, thank you.
Finished writing on Thursday, April 11, 2024 at 13:39, the second day of Eid.


