This is an approach to navigation using the Next.js 13 App Router that will step around the client cache rules. It addresses the concerns described at this monster GitHub issue: vercel/next.js#42991.
My, you're in a hurry! Please read the rest but to get started:
- Wrap the entire app in the server-rendered
CacheBusterContainer
. - Anywhere we have a link that we do not want to cache, we use
ExtendedLink
instead of link.
That's it.
The Next.js App Router has opinionated rules about caching. Among them is a default by which any page visited by following a Link
(<Link href={somePath}>
) will be held in a client-side cache for 5 minutes or until the cache is manually cleared using revalidatePath
. The only supported alternative to this is setting prefetch={false}
, which reduces the 5 minute cache to 30 seconds. For many uses cases, 30 seconds is too long.
Consider the following ecommerce site scenario:
- A customer visits a product page. They look at it for a monent and then browse back to look at more items.
- While they are elsewhere, the product sells out.
- They return to the page but they see the cached result. This is held in the client -- there is no way for them to know the content is expired. They try to add it to their cart and receive an error message. They think the site is broken, they leave.
What we would like to see happen: they return to the page, it renders new content from the server, they see it is sold out, they are sad they missed their opportunity.
- The server container's job is to pass an action,
revalidateAction
, down to the client container. This works around a bug, something about cache... something. I don't have it in front of me. Try it without the server component and see. :-) - The client container provides a context object. This context object exposes a callback that accepts a string of a path to invalidate.
- When an
ExtendedLink
is clicked, it:
- Calls the callback defined by the client container. It provides the path of the link that was just clicked.
- The client container also has a mutable ref of type
string[]
. When the callback is called, the pathname provided as an argument is added to this array. - The client container has a
useEffect
that is triggered when the pathname changes. On every pathname change, it compares the head (element 0) of the mutable ref to the new path. If element 0 is not empty and it does not match the new path, it calls the server action which wrapsrevalidatePath
.
In other words, we hold a list of routes that we want to invalidate (ExtendedLink
links clicked) and we wait until someone leaves the paeg before we invalidate them.
A handful of alternatives exist. You can read about some of them here. I provided instructions on using patch-package here.
- You're using a server action. Server actions are in alpha, the interface might change.
- Your server will be doing more work. If you're on a serverless platform, this will make your functions work harder. That could be a problem! Especially if you're on Vercel's free tier, which apparently limits your
revalidatePath
calls to 100 per month. Be careful! - I have not tested this thoroughly. I do not know what potential side-effects it might cause. Please use this carefully!