revalidateTag in a server action in a suspended server component
Unanswered
Scrumplex posted this in #help-forum
I have a Server Component (wrapped in Suspense) that calls a Server Action that will revalidate a certain tag. This seems to cause a server side error like this:
The use case is this: A user clicks a link in an email, and we want to show them a loading page until the action has been completed. I assume I could refactor this to use loading.ts/loading.ts at some point.
I have a minimal reproduction here: https://codesandbox.io/p/devbox/unruffled-wilbur-dzvdyp?workspaceId=ws_DMWHE5EnmeHg9xHxT5di14
This worked some time ago. Is this unsupported now?
[ Server ] Error: Route / used "revalidateTag foobar" during render which is unsupported. To ensure revalidation is performed consistently it must always happen outside of renders and cached functions. See more info here: https://nextjs.org/docs/app/building-your-application/rendering/static-and-dynamic#dynamic-rendering
The use case is this: A user clicks a link in an email, and we want to show them a loading page until the action has been completed. I assume I could refactor this to use loading.ts/loading.ts at some point.
I have a minimal reproduction here: https://codesandbox.io/p/devbox/unruffled-wilbur-dzvdyp?workspaceId=ws_DMWHE5EnmeHg9xHxT5di14
This worked some time ago. Is this unsupported now?
8 Replies
server actions are supposed to be called from the client, not during server component rendering.
in this instance,
it will act as a server action and
it will just act as a regular javascript function and
why do you use a server action here in the first place and why do you need to revalidate anything here?
"use server";
export async function foo() {
...
}
in this instance,
"use client";
<button onClick={() => foo()}>
Click me
</button>
it will act as a server action and
revalidateTag
will work, but in this instance,async function ServerComponent() {
await foo();
// ...
}
it will just act as a regular javascript function and
revalidateTag
will not work because you cannot doasync function ServerComponent() {
revalidateTag("foo");
}
why do you use a server action here in the first place and why do you need to revalidate anything here?
I wrote a server action that updates a resource in an external application. Other parts of the code use Next Fetch to cache those resources and by updating them I want to revalidate the data. Currently this isn't an interactive action, but rather relies on a magic link sent in an email. Once the user visits the link I want to update the resource and revalidate cache
1. User clicks on magic link
2. Next.js shows a loading page (achieved by using Suspense around the (slow) Server Component)
3. The Server Component calls the slow API and revalidates cache
4. The Server Component renders a success/failure message
2. Next.js shows a loading page (achieved by using Suspense around the (slow) Server Component)
3. The Server Component calls the slow API and revalidates cache
4. The Server Component renders a success/failure message
hmm you can't revalidate something while rendering something else, so i think this is the simplest approach:
* just return the page immediately
* in the page, using
* when the server action resolves, set a state or something like that to remove the loading state
* just return the page immediately
* in the page, using
useEffect
or something similar, call the server action there* when the server action resolves, set a state or something like that to remove the loading state
the "correct" approach here is to make it a route handler instead and run mutation+revalidation there, after that redirect the user to a success page – but this assumes the data update is fast and the approach is no longer usable when a loading screen is necessary
I see. I personally don't like abusing useEffect for things like this. I might just change the magic link to require additional confirmation by clicking a button or something.
It seems like this was an intentional breakage in Next.js 15: https://github.com/vercel/next.js/pull/71093
It seems like this was an intentional breakage in Next.js 15: https://github.com/vercel/next.js/pull/71093
yeah well technically what you are doing is exactly a side effect during render.
useEffect
doesn't feel good here but you don't really have an alternative.it's a legitimate use case, yes, but it is still a side effect during render – a mutation done inside a GET call