ProNextJS
    Loading
    lesson

    Handling Headers and Parameters In NextJS 15

    Jack HerringtonJack Herrington

    Next.js 15 introduced some changes to how we handle parameters and headers. We'll explore these changes and learn how to implement them using dynamicIO.

    Headers

    First, let's take a look at headers. We want to get the headers from the incoming request and avoid caching them. To do this in Next.js 15 with dynamicIO, we need to wrap our code in a suspense boundary. This is different from Next.js 15 without dynamicIO, where you wouldn't need to add a suspense boundary.

    Here's an example of how to wrap your code in a suspense boundary:

    async function Headers() {
    	const out = await headers();
    	const keys = Object.keys((out as unknown as { headers: object }).headers);
    	return <div>Headers Dynamic: {keys.join(", ")}</div>;
    }
    
    export default async function HeadersDynamic() {
    	return (
    		<Suspense fallback={<div>Loading...</div>}>
    			<Headers />
    		</Suspense>
    	);
    }
    

    After saving, you should be able to see the headers without any errors.

    Parameters

    Next, let's examine parameterized routes. In Next.js 15, there's a significant change in how parameters are handled. Instead of receiving parameters as an object like in Next.js 14:

    export default async function Params({ params }: { params: { id: string } }) {
    	const { id } = params;
    	return <div>Params: {id}</div>;
    }
    

    In Next.js 15, parameters are now received as a promise for performance reasons:

    export default async function Params({
    	params,
    }: {
    	params: Promise<{ id: string }>;
    }) {
    	const { id } = await params;
    	return <div>Params: {id}</div>;
    }
    

    Keep in mind that you wouldn't want to cache these parameters.

    Sometimes, dynamic imports may have issues with parameterized routes. If this happens, you can use Suspense to fix it.

    To do so, follow these steps:

    1. Create a new component for a non-default route.
    2. Create a new ParamsDisplay component and fetch the data using a Promise.
    3. Wrap the fetched data in a Suspense component.
    import { Suspense } from "react";
    
    async function ParamsDisplay({ params }: { params: Promise<{ id: string }> }) {
    	const { id } = await params;
    	return <div>Params: {id}</div>;
    }
    
    export default async function Params({
    	params,
    }: {
    	params: Promise<{ id: string }>;
    }) {
    	return (
    		<Suspense fallback={<div>Loading...</div>}>
    			<ParamsDisplay params={params} />
    		</Suspense>
    	);
    }
    

    Transcript

    Next.js 15 also made some changes to how we do parameters as well as headers. So let's go and take a look at how to implement on both of those. All right so again we're going to take a look at our stock 15 and grab two more examples. In this case headers dynamic and params. Copy those over to our dynamic IO.

    Now, let's take a look at headers first. So here, we just want to get the headers from the incoming request. So we want to get those, we never want to cache the headers. I don't think there's any circumstances where you'd want to actually cache headers. So let's just make sure that we can do that in Next.js 15 with Dynamic IO.

    So let's go over to headers dynamic And then we can see that we do get the headers, but we also get an error, and that error is around the fact that we need to add a suspense boundary. So for 15 without Dynamic I-O, you would not need to do this, but for 15 with Dynamic I-O, but because this is a dynamic thing we want to wrap that in a suspense Let's save and now we get our headers and no error Okay, let's take a look at parameters. So these are parameterized routes in this case. We've got params with an ID and in 15 there's a big change right off the bat and that's where we used to in 14 just get these as a object like that. In 15 we now get them as a promise and that's apparently for performance reasons.

    Again this is not something you'll ever want to cache but let's hit save and try it again. You can see up in the url over here that you get params1 a parameterized route we can make that whatever we want. Fine. But let's take a look at this. So it's not having an issue.

    I have seen Dynamic I.O. Have an issue with this. So if you get that, what you want to do is do the suspense thing again. So we'll go and make that the non-default route, and we'll create a new params display, and we'll get our promise, and then we'll wrap that in a suspense. There you go.

    So params is going to invoke params display, and params display is going to do the await, and we're going to wrap that all in a suspense because we never want to cache this and yeah that still works. So if you see that issue around the params where you don't see it on a non parameterized route then you'll want to do a suspense like this to make sure that as you're getting that dynamic params data that you add a suspense around it.