nextjs - ✅(Solved) Fix [cacheComponents] Activity component route preservation causes significant breakage in application logic, UI behavior and E2E tests [1 pull requests, 70 comments, 27 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Error Message

Error: Calling setState synchronously within an effect can trigger cascading renders Avoid calling setState() directly within an effect

Root Cause

This reproduction demonstrates 3 issues caused by Activity-component–driven route preservation when cacheComponents: true is enabled

Fix Action

Fix / Workaround

Each issue now requires manual workaround logic. For example, controlling the dialog open state requires syncing it inside a useEffect because the component no longer remounts. That workaround triggers a React ESLint warning:

PR fix notes

PR #1: Address issues from Activity

Description (problem / solution / changelog)

Hey @SorooshGb!

First, I wanted to say thank you for taking the time to make this reproduction + write up in the corresponding issue.

We dropped the ball on properly communicating the Activity changes that came along with Cache Components, and also following up more quickly with a response to your concerns. We're really excited that you + others are eager to try out Cache Components, so apologies again for the radio silence.

I responded in the issue with a writeup of explaining how this PR addresses the issues you raised, as well as some additional context for why we made the change in the first place:

https://github.com/vercel/next.js/issues/86577#issuecomment-3801284197

I hope it helps answer your concerns + looking forward to continuing the discussion over there.

Changed files

  • e2e/signUp.spec.ts (modified, +3/-3)
  • eslint.config.mjs (modified, +3/-15)
  • package-lock.json (modified, +14/-0)
  • package.json (modified, +1/-1)
  • src/components/EntryDialog.tsx (modified, +21/-9)
  • src/components/ItemDropdown.tsx (modified, +16/-6)

Code Example

Binaries:
  Node: 22.16.0
  npm: 11.4.2
  Yarn: 1.22.22
  pnpm: 10.13.1
Relevant Packages:
  next: 16.0.5 // Latest available version is detected (16.0.5).
  react: 19.2.0
  react-dom: 19.2.0
Next.js Config:
  output: N/A

---

Error: Calling setState synchronously within an effect can trigger cascading renders
Avoid calling setState() directly within an effect

---

page.getByLabel("Email").filter({ visible: true })
RAW_BUFFERClick to expand / collapse

Link to the code that reproduces this issue

https://github.com/SorooshGb/next16-activity-breakage-repro

To Reproduce

This reproduction demonstrates 3 issues caused by Activity-component–driven route preservation when cacheComponents: true is enabled

  1. Clone the reproduction repo linked above.

  2. Install dependencies: npm install npx playwright install chromium

  3. Start the dev server: npm run dev

  4. Test the following behaviors in the browser:

A. Dropdown stays open

  • On the home page, open the dropdown once. In this reproduction, the dropdown’s default close-on-select behavior is intentionally prevented because of application logic requirements.
  • Click any dropdown button that navigates to an Item page.
  • Navigate back to the home page.
    → The dropdown is still open because the component never unmounts.

B. Dialog behavior breaks because mount-time logic never runs again

Each row in the dropdown contains two navigation buttons:

  • “View Item” (eye icon) → navigates to /item/[id] without search params
  • “Add Item” (plus icon) → navigates to /item/[id]?newEntry=true

Expected behavior:

  • “Add Item” → dialog should open on arrival
  • “View Item” → dialog should remain closed

Actual behavior:

  1. Reload the home page to start clean.
  2. Open the dropdown and click “Add Item” (?newEntry=true).
    → Dialog opens as expected.
  3. Use the browser Back button to return home.
  4. Click “View Item” (no newEntry param).
    → The dialog is still open even though the URL does not include the param.

Now demonstrate that once closed, the dialog never opens again:

  1. Reload the home page again.
  2. Open the dropdown and click “Add Item” (?newEntry=true), then close the dialog manually.
  3. Navigate back to the home page.
  4. Open the dropdown and click “Add Item” again (?newEntry=true).
    → The dialog does not open, even though the URL includes the param.

This shows that mount-time initialization (including logic based on search params) only runs once, and the dialog’s open state becomes permanently tied to stale state.

C. E2E test fails due to duplicate DOM

With the dev server still running, execute:

npm run test:e2e

This test visits /sign-in first (which contains Email and Password fields), then clicks the link to navigate to /sign-up (which also contains Email and Password fields).

Because Activity keeps the previous route mounted, the hidden /sign-in page remains in the DOM when /sign-up loads, resulting in two Email fields and two Password fields existing at the same time.

→ Playwright strict mode fails because getByLabel("Email") and getByLabel("Password") match multiple elements, triggering strict-mode violations.

Current vs. Expected behavior

Expected Behavior: With cacheComponents disabled, the app behaves correctly:

  • Components reset normally (dropdown closes, dialog opens/closes based on initial state passed to it)
  • E2E selectors match only the elements on the active page

This is the behavior the app always had before Next.js 16 — including when using the older use cache / PPR canary features — none of which introduced any Activity component behavior.

Current Behavior: With cacheComponents: true, Next.js preserves previous routes using Activity:

  • Components do not unmount when navigating
  • Hidden DOM from previous pages remains present
  • UI state persists across unrelated pages (dropdown stays open)
  • Dialog components do not re-run mount-time logic
  • E2E tests fail because multiple matching fields are in the DOM

Provide environment information

Binaries:
  Node: 22.16.0
  npm: 11.4.2
  Yarn: 1.22.22
  pnpm: 10.13.1
Relevant Packages:
  next: 16.0.5 // Latest available version is detected (16.0.5).
  react: 19.2.0
  react-dom: 19.2.0
Next.js Config:
  output: N/A

Which area(s) are affected? (Select all that apply)

cacheComponents

Which stage(s) are affected? (Select all that apply)

next dev (local), next start (local), Vercel (Deployed), Other (Deployed)

Additional context

All issues mentioned above disappear immediately if cacheComponents is set to false, but doing so also disables the new "use cache" and PPR features.

After upgrading to Next.js 16 and enabling cacheComponents: true, unexpected behavior began to appear in my application. The first issue was the portal visibility bug (which I also reported here: https://github.com/vercel/next.js/issues/85390). React fixed that bug upstream, and it was resolved in Next.js 16.0.2. However, after upgrading, additional issues still surfaced.

Because Activity changes fundamental assumptions about route lifecycle, there is a high chance that more unexpected behaviors will appear in applications that rely on normal unmount-on-navigation semantics.

Each issue now requires manual workaround logic. For example, controlling the dialog open state requires syncing it inside a useEffect because the component no longer remounts. That workaround triggers a React ESLint warning:

Error: Calling setState synchronously within an effect can trigger cascading renders
Avoid calling setState() directly within an effect

for the E2E test to pass, the Playwright selectors must be changed to:

page.getByLabel("Email").filter({ visible: true })

This avoids strict-mode violations caused by duplicate hidden fields, even though Playwright generally advises against relying on .filter({ visible: true }) as a primary testing strategy.

extent analysis

TL;DR

Disabling cacheComponents or implementing manual workaround logic for affected components can mitigate the issues caused by Activity-component-driven route preservation in Next.js 16.

Guidance

  • Identify components that rely on normal unmount-on-navigation semantics and implement manual workaround logic to handle persistent state and unexpected behavior.
  • Use useEffect to sync state and handle mount-time logic, but be aware of potential React ESLint warnings and cascading renders.
  • Update Playwright selectors to account for duplicate hidden fields, such as using filter({ visible: true }) to avoid strict-mode violations.
  • Consider disabling cacheComponents if the issues cannot be resolved through workaround logic, but be aware that this will also disable the new "use cache" and PPR features.
  • Review the application's reliance on route lifecycle assumptions and update the code to accommodate the changes introduced by Activity in Next.js 16.

Example

useEffect(() => {
  // Sync dialog open state here
  // This may trigger a React ESLint warning, so use with caution
}, [dependentState]);

Notes

The provided guidance is based on the issues reported and may not cover all possible scenarios. The introduction of Activity in Next.js 16 changes fundamental assumptions about route lifecycle, so it's essential to review the application's code and update it accordingly.

Recommendation

Apply workaround logic to affected components, as disabling cacheComponents may not be desirable due to the loss of new features. Implementing manual workaround logic will allow you to leverage the benefits of Next.js 16 while mitigating the issues caused by Activity-component-driven route preservation.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

nextjs - ✅(Solved) Fix [cacheComponents] Activity component route preservation causes significant breakage in application logic, UI behavior and E2E tests [1 pull requests, 70 comments, 27 participants]