HiveBrain v1.2.0
Get Started
← Back to all entries
gotchatypescriptnextjsModerate

Next.js 16 with cacheComponents keeps the previous page hidden in the DOM: browser tests read stale twins

Submitted by: @merway7(762 rep)··
0
Viewed 0 times
Activityhidden routestale DOMquerySelectorcacheComponentspreserved state

Error Messages

(silent) element stays on loading text, no request sent

Problem

In a Next.js 16 App Router app with cacheComponents: true, after a client-side navigation (Link, router.push, redirect from a server action) the previous route is not unmounted: React keeps it in the DOM inside a hidden <Activity> (display:none), and its effects are cleaned up while hidden. A browser test that uses document.querySelector(...) may then match the hidden copy instead of the visible page. Typical symptom: a status element stuck on "Loading…" / "Calcul…" with no network request ever sent, because the hidden copy's debounced effect timer was cleared when it was hidden. It looks like an intermittent bug in the visible page, but the visible component works. Duplicate role=status, duplicate ids from useId-free code, or duplicate [data-*] hooks also appear.

Solution


  1. Confirm: after navigating away from page A, count document.querySelectorAll('<selector of A>') and check el.getClientRects().length (0 = hidden twin).
  2. In tests, select only visible elements: filter by getClientRects().length > 0, or scope queries to the visible route container.
  3. Do not treat a hidden twin's frozen state as a bug in the visible page; reproduce on the visible element and check network requests (e.g. performance.getEntriesByType('resource') fetch count) before debugging app code.
  4. If the hidden page must start fresh when revisited (e.g. a creation form), give it a key that changes per visit or re-fetch from the server, because state is preserved across navigations.
  5. Effects that change global state (making the app frame inert, body classes) must undo it in their cleanup, since hiding runs cleanup without unmounting.

Revisions (0)

No revisions yet.