A route transition changes both what is displayed and where a person is operating. Browser-managed scroll restoration works for many document navigations, but a client router that swaps content without a full navigation must define its own policy. On a new detail route, move focus to a useful heading or main region and usually start at its beginning. On Back to a long queue, restore the prior position after the correct list content and layout are available. A raw pixel offset is fragile when rows change height; a stable row identity with an offset can be more reliable. Focus and scroll are separate: setting one does not guarantee the other remains appropriate.
Route Scroll and Focus Restoration
Working case
A keyboard user opens case 47 from row 31 of the Pending queue. The client router replaces the main panel but leaves focus on the removed row, so the next Tab starts somewhere surprising. When Back returns, the queue refetches and the browser restores the old pixel position before rows exist, landing at the top. Record the row and filter in the history entry, focus the case heading after the new view is ready, and restore the queue anchor only after the list has its current rows. If row 31 was removed, choose the nearest surviving row and announce the change.
Implementation boundary
function restoreTarget(rows, requestedCaseId) {
const exact = rows.find(row => row.caseId === requestedCaseId);
return exact ?? rows[0] ?? null;
}
console.log(restoreTarget([{ caseId: 47 }, { caseId: 62 }], 62).caseId);
// Output: 62Keep focus movement tied to navigation intent, not every rerender. Mark the heading or main region programmatically focusable when needed, then restore normal tab order. For scrolling, record a stable anchor before leaving and wait until the destination content and fonts have settled enough to position it. Avoid disabling native scroll restoration globally unless the router actually takes over the whole responsibility. When the queue changes, fall back to a nearby valid row rather than forcing a stale pixel coordinate. Test with keyboard, screen reader, slow data, and variable row heights.
Cost and boundaries
Finding an anchor in n rendered rows is O(n) with a linear scan or near O(1) through an existing index; storing one anchor per history entry is small. Waiting for data and layout can delay restoration, while an immediate pixel jump may be wrong. Virtualized lists add complexity because the anchor might not be mounted yet. A heading focus operation is cheap but can unexpectedly scroll the page, so sequence and measure the visual result. Preserve user control if the person scrolls during a delayed restore.
Failure trace
A route handler always calls window.scrollTo(0, 0), including on Back. The reviewer loses the queue row they were working from. Another handler restores a saved pixel coordinate after a late data fetch even though the person has already scrolled manually; the page jumps out from under them. Distinguish new navigation from history traversal, restore by stable content when possible, and cancel a pending restoration after user scroll input. Check focus as well as viewport position.
Verification
- New detail navigation places focus on its new content.
- Back returns near the prior queue row after delayed loading.
- A manual scroll cancels a pending automatic jump.
Practice drill
Start on queue row 31, open case 47 with keyboard, press Back, and inspect both focus target and scroll anchor. Delay queue data by two seconds, then scroll manually before it arrives; the delayed restoration must not override that choice. Change row height through a longer translated title and repeat. Remove the original row while away and verify a predictable fallback. Try 400% zoom so focus rings and scroll targets remain visible.
Decision note
Preserve navigation context with stable content identity and explicit focus rules, then respect new user input during delayed restoration.
Common Mistakes
- Resetting scroll on every route change.
- Restoring a pixel offset before variable content loads.
- Moving scroll while leaving focus in a removed element.
Connected lessons
Navigation, History, and Page Lifecycle; URL State and History Entry Contracts; Back-Forward Cache and Restored State; Navigation Prefetch and Private Data Budget; Accessible forms: connect labels, errors, and focus; Reflow, Zoom, and Motion Preferences; Browser Routing and History State.
Apply and check
Build Project: history-safe review journey and review Web Development: navigation and delivery decisions quiz.
Further connections
Scroll Containers, Anchoring, and Chaining; Visual Viewport, Keyboard, and Focused Input.
