How to Pass and Receive Query Parameters in Next.js 14 Server Components with Middleware

waffensultan

Waffen Sultan

Posted on October 23, 2024

How to Pass and Receive Query Parameters in Next.js 14 Server Components with Middleware

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

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
Enter fullscreen mode Exit fullscreen mode

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>
    )
}
Enter fullscreen mode Exit fullscreen mode

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
            }
        })
    }
}
Enter fullscreen mode Exit fullscreen mode

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>
    )
}
Enter fullscreen mode Exit fullscreen mode

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/

๐Ÿ’– ๐Ÿ’ช ๐Ÿ™… ๐Ÿšฉ
waffensultan
Waffen Sultan

Posted on October 23, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

ยฉ TheLazy.dev

About