[cache components]: Client component's state during route navigation #85502
-
|
In Next.js 16, when cacheComponents is enabled, the client component's state is preserved during route navigation. However, when cacheComponents is disabled, the state is lost. What causes this behavior? |
Beta Was this translation helpful? Give feedback.
Replies: 9 comments 14 replies
-
|
When Cache Components is enabled, we also wrap Activity components around routes. I just merged the docs update w/ this behavior. Be aware that there's a known issue with Portals. Tracked upstream here: facebook/react#35000 |
Beta Was this translation helpful? Give feedback.
-
|
We're experiencing this Portal issue with shadcn/ui Sidebar (which uses Radix Dialog) in a route group layout scenario: Scenario:
Main UI Bug:
This happens because:
Current Workaround: useEffect(() => {
return () => {
// Wait 500ms for React's async cleanup attempt
setTimeout(() => {
// Manually remove leftover Portal DOM
const sidebarElements = document.querySelectorAll('[data-slot="sidebar"]');
sidebarElements.forEach(element => {
const portalContainer = findPortalContainer(element);
if (portalContainer?.parentNode && document.body.contains(portalContainer)) {
portalContainer.parentNode.removeChild(portalContainer);
}
});
}, 500);
};
}, []);Questions:
@icyJoseph Thanks for the clarification on Cache Components behavior! 🙏 |
Beta Was this translation helpful? Give feedback.
-
|
Is there currently no way to opt out of this? Are there plans to add the option to opt out? |
Beta Was this translation helpful? Give feedback.
-
|
Just saw this been struggling with custom solutions for awhile and could not figure out why my unmounting was not working. This breaks so much existing code it definitely should not be the default. |
Beta Was this translation helpful? Give feedback.
-
|
It’s been a major pain for us as well, and we eventually had to completely opt out of cacheComponents. Our applications have hundreds of dedicated form routes, and once they’re implicitly wrapped in and no longer unmount on navigation, everything breaks from a UX and logic standpoint. It’s not just form state that gets preserved, all useState values are kept alive, even in places where unmounting has always been the natural reset mechanism. A concrete example: With cacheComponents enabled, after that first submit, every future navigation to that same parallel route reuses the stale component state. So instead of opening the dialog again, it instantly triggers the success toast and router.back(), making the dialog impossible to access. The component effectively becomes “stuck” in its previous state forever. Issues like this quickly compound across a large codebase. Retrofitting manual reset logic for hundreds of forms, dialogs, drawers, and components that naturally expect to unmount is simply not realistic. State preservation on navigation can absolutely be useful, but not as an implicit global behavior that requires every component to suddenly become “Activity-aware”. Right now it feels forced on us in a way that breaks long-established patterns, very similar to the aggressive caching in Next 13. I really think this needs to be opt-in, or at least offer a clear and granular opt-out at the route/layout level. These features are powerful, but without an escape hatch they can be extremely disruptive to existing, fully correct application logic. |
Beta Was this translation helpful? Give feedback.
-
|
Want to anecdotally contribute that persisting state between navigation can undesirably leak user data. I've encountered instances wherein a user on the same tab signs in, performs behaviors that are stateful, logs out, and another user logins during that same local session, leads to the recent user being able to see the previous user's existing state. With the mechanisms of react activity when cacheComponents are enabled, developers will need to make significant refactors (in breadth) to ensure sensitive state at the very least is reinitialized appropriately under those circumstances. |
Beta Was this translation helpful? Give feedback.
-
|
Tbh i don’t understand how this continuing to be the default behaviour is even an option. It basically makes cacheComponents unusable on most projects. |
Beta Was this translation helpful? Give feedback.
-
|
This change is highly frustrating. I do not understand the rationale behind altering React’s default behavior to preserve client state across navigations. |
Beta Was this translation helpful? Give feedback.
-
|
I don't understand how this can be considered as default behaviour without an option to opt-out. Wrapping routes inside Activity to preserve state should be TOTALLY opt-int. In the old model without Activity wrapped routes (cacheComponents disabled), the data for this route gets loaded inside a Suspense, then propagated to the client side form. This lead to fresh data when you click a data object in the table - because data got dynamically loaded inside an RSC every time, and client state got re-created on a new route visit. In the new model WITH Activity wrapped routes, everything about this behaviour is broken. You essentially cannot use client side libraries for form state management anymore. More detailed: If i click on a data object the first time, the route is visited and the form is prefilled correctly with fresh data. If i now go back and someone else/or something server side leads to a data change in that object and then i revisit that route i still see my old data due to client state preserving. Even though the new changed data is correctly fetched inside the RSC, its not getting applied correctly since the client state is not re-initialized with the new data, therefore i see my old out-of-date client state. And there is actually no way to change this behaviour at the moment without working around this with some sketchy manual client state clearing. I don't think this is what you wanted to achieve with cacheComponents in the first place - you wanted to deliver more control and flexibility about what is getting cached and what not, and then force us to cache ALL client state? This isn't flexible at all and basically makes cacheComponents unusable outside of the lab. Please make this Activity client state thing opt-in on a route level and then write a BIG section about it in the docs for cacheComponents. |
Beta Was this translation helpful? Give feedback.
When Cache Components is enabled, we also wrap Activity components around routes. I just merged the docs update w/ this behavior. Be aware that there's a known issue with Portals. Tracked upstream here: facebook/react#35000