Focus Management in the Next.js App Router: Field Notes on the Route Change That Loses Focus, inert, and Native dialog
The Next.js App Router announces client-side route changes to screen readers through its built-in route announcer, but it never moves keyboard focus. Focus is left on a link that React just unmounted, so the next Tab press restarts at the top of the document — and the fix is a fifteen-line client component plus one tabIndex={-1} . Key takeaways Next.js injects a route announcer on every…
The Next.js App Router introduces a client-side route announcer for screen readers, which reads the new document.title but does not handle focus changes. When navigating client-side, focus is left on the link that React just unmounted, causing the next Tab press to restart at the top of the document. To address this, a fifteen-line client component can be added to manage focus after route changes.
This component uses usePathname() from next/navigation to detect changes and calls .focus({ preventScroll: true }) on a container with tabIndex={-1}, making it focusable via script while remaining invisible in the Tab order. The inert attribute, when added to a subtree, removes it from the Tab order, accessibility tree, and pointer hit testing.
The native dialog element provides a focus-trapped modal experience when opened imperatively with showModal(), rendering the ::backdrop pseudo-element and making everything outside inert. However, rendering a dialog open in JSX results in a non-modal dialog without a focus trap or backdrop.
Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.