I was developing a full-stack web application project in Next.js when I stumbled upon a problem with retrieving query parameters in Server Components.
My Use Case and Problem
My use case is as follows: In my Server Component (layout.tsx
), I check the URL for any query parameters, retrieve either regular or unfiltered data from the backend/API based on those parameters, and pass the data as props to my Client Component (page.tsx
). If the user chooses to apply filters, which will be reflected as query parameters in the URL, the page gets refreshed so that my Server Component (layout.tsx
) can look for any query parameters and pass down new data.
A rough diagram of my use case described above
Next.js recommends for most backend action to happen in the server (Server Components, Server Actions, Route Handlers, etc.) and I found it confusing on how you can easily retrieve query parameters in Client Components with the useSearchParams
client-side hook but not in Server Components.
It left me wondering where else or how'd they typically be used by other developers in Client Components, given the emphasis on server-side backend functionality but that's probably a topic for a later conversation.
After searching the internet for answers, here is what I went with:
Solution: Passing Query Parameters in the Middleware
In this example, we will be using Tailwind CSS and TypeScript.
We're building a simple website where the Client Component allows users to input their gender, which is sent as a query parameter in the URL. The Middleware then intercepts the request, processes the query parameter of gender
, which we then retrieve from the Server Component. Depending on the value of the query parameter gender
, the page changes colours (turns pink if female, blue if male).
If you're following along and haven't already created a new project in Next.js, enter this command into your preferred CLI:
npx create-next-app@latest your-project-name
First, let's setup our Client Component, page.tsx
:
export default function Page() {
/* Create a state wherein we store the gender of our user */
const [gender, setGender] = useState<string | undefined>(undefined);
const router = useRouter();
const handleSubmit = (currentGender: string | undefined) => {
/*
* Here, we attach/replace our URL with our query parameter
* Query parameters follow the format: key=value
*/
router.replace(`/?gender=${currentGender}`);
/* Trigger a re-fetch of data from our Server Component */
router.refresh();
}
return (
<div className="w-screen h-screen flex flex-col gap-5 flex justify-center items-center">
<input
value={gender}
onChange={(e) => setGender(e.target.value)}
placeholder="Enter your gender here..."
className="rounded-md px-2 text-background focus:outline-none"
/>
<button onClick={() => handleSubmit(gender)}>submit gender</button>
</div>
)
}
Secondly, let's setup our middleware.ts
:
export function middleware(request: NextRequest) {
/*
* Here, we retrieve the query parameters in our URL.
* `nextUrl.searchParams` is a Next.js method/function that extends URLSearchParams
* (https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams)
*/
const queryParams = request.nextUrl.searchParams;
/* Retrieve the value of the query parameter `gender` which we retrieve from our Client Component */
const gender = queryParams.get('gender');
if (gender !== null) {
const requestHeaders = new Headers(request.headers);
/* Attach `gender` to our header */
requestHeaders.set('user_gender', gender);
/*
* Return `requestHeaders` as our new header
* In our Server Component (layout.tsx), we can retrieve the `gender` value
* by accessing the `user_gender` field in our header
*/
return NextResponse.next({
request: {
headers: requestHeaders
}
})
}
}
And, finally, our Server Component, layout.tsx
:
export default function Layout({ children }: Readonly<{ children: React.ReactNode }>) {
/* Retrieve the headers sent to us from the Middleware */
const headersList = headers();
/*
* Retrieve the value of the user's gender from
* the field which we previously declared: `user_gender`
*/
const userGender = headersList.get('user_gender');
return (
<html lang="en">
<body className={`${userGender === 'male' ? 'bg-blue-500' : 'bg-pink-500'}`}>
{children}
</body>
</html>
)
}
Conclusion
I hope you found my first technical article insightful and helpful. If I had made any mistakes, please feel free to let me know by making a comment. Thanks for reading!
About Me
I am a freshman Computer Science student at Cavite State University. I like coffees and pandas!
Connect with me:
GitHub: https://github.com/waffenffs
LinkedIn: https://www.linkedin.com/in/wffnsltan/
Top comments (0)