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
@Scrumplex 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:
[ 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?
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?
@joulev server actions are supposed to be called from the client, not during server component rendering.
tsx
"use server";
export async function foo() {
...
}
in this instance,
tsx
"use client";
<button onClick={() => foo()}>
Click me
</button>
it will act as a server action and `revalidateTag` will work, but in this instance,
tsx
async function ServerComponent() {
await foo();
// ...
}
it will just act as a regular javascript function and `revalidateTag` will *not* work because you cannot do
tsx
async 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
@Scrumplex 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
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