skip to content

After a client-side router commits a navigation, how does it decide the new screen's scroll position, focus and title?

level: seniorimportance: should knowfreq 46%

answer

  1. a commit resets nothing by itself
  2. policy per navigation kind
  3. key offsets by entry, not address
  4. capture before teardown, restore after content
  5. disable the automatic restoration first

basics

~20 s

A client-side commit resets nothing by itself, so the router needs a policy: top for a new destination, the saved offset for that history entry on a traversal, unchanged for a refinement, focus and title per route.

solid answer

~50 s

A full document load resets scroll, resets focus to the start of the document and takes the title from the markup. A client-side commit does none of that, so the router needs a policy. Scroll: top for a new destination, the saved offset for a traversal, unchanged when the navigation only refines the current location, and the target element for a fragment. The offsets are the router's to store, keyed by **history entry** rather than by address, captured when a transition starts — the same address can sit at two entries with different positions. The browser's own automatic restoration has to be switched off first, or it restores against content that does not exist yet and then fights the router. Focus must move to the new content instead of being lost with the element that was removed, and the title is set per route because it names the history entry.

go deeper

for a junior

Remember that a client-side navigation does not reset scroll, focus or title by itself, and that a new destination should start at the top while Back should return to where the user was.

for a middle

Explain the per-navigation-kind policy, that the router saves offsets itself when a transition starts, and that a refinement of the current address should not move the page at all.

for a senior

Show the failure modes you have fixed: an offset clamped because it was restored before the content had height, the double jump from leaving the automatic restoration on, and offsets keyed by address instead of entry.

for a principal

Own it as one cross-cutting behaviour rather than per screen: where the policy lives, how a scrolling panel declares itself, and what the app guarantees for content that streams in after commit.

## Nothing resets for free A document navigation does three things the browser never charges you for: the new document starts at the top, focus starts at the beginning of the document, and the title comes from the markup that arrived. A client-side commit replaces a subtree of a document that never went away, so **all three of those carry over from the previous screen** unless the router acts. The symptoms are familiar from real apps: a detail screen opened from halfway down a list appears already scrolled into its middle; a keyboard user activates a link and their focus point vanishes with the element that was removed; the tab and the back-button menu still name the screen the user left. So the question is not *how* to scroll but *what the policy is* — and the policy is per navigation kind. ## The scroll decision | Navigation | Scroll behaviour | Why | |---|---|---| | A new destination the user chose | Top of the new content | They have not been here; there is no position to return to | | Back or forward to an existing entry | The offset saved for **that entry** | The user is returning to a view they remember | | A refinement of the current address, such as a filter or a sort | Leave the position alone | The user did not go anywhere | | An address carrying a fragment | The position of the named element | The address itself asks for a place in the document | | A change confined to one region inside a persistent layout | Restore or reset that region's scroller | The document may not be the thing that scrolls | That last row matters more than it looks. When the scrolling happens in a panel rather than the document, restoring the document's offset achieves nothing, and the router's storage has to name which scroller an offset belongs to. ## Who stores the offsets, and how they are keyed 1. **The router stores them**, in memory for the session, as part of what it tracks per navigation. 2. **Key by history entry identity, not by address.** The same address can occupy two entries at different depths of the stack with different positions, and a user who traverses between them expects each to come back as they left it. 3. **Capture at the moment a transition starts**, before the outgoing screen is torn down. Reading the offset after the commit reads the new screen. 4. **Restore after the content that determines the height exists**, not at commit time. This is the step teams get wrong: setting an offset of 4,000 pixels on a container that is currently 600 pixels tall clamps to the bottom, and the user lands nowhere near where they were. For a screen whose content arrives in pieces, one restore is not enough — either re-apply as the height grows, hold the position against content inserted above, or delay the reveal until enough of the content is present to be measured. ## Why the browser's automatic restoration has to be turned off The platform has its own scroll restoration for traversals, designed for documents that are re-created by the navigation. In a client-rendered app it acts on a document whose new content has not been rendered yet, so it restores against the wrong height, and then the router's own restore arrives and moves the page a second time — a visible double jump. An app that wants correct restoration disables the automatic behaviour and takes full ownership. The decision to opt out is the router's, and it has to be made before the first traversal. ## Focus and title at commit - **Focus.** When the activated element is removed by the navigation, the focus point is lost and keyboard users are returned to the start of the document, forced to traverse the whole shell again on every navigation. The router's job is to place focus deliberately on the new content, or on a skip target at the top of it, as part of the commit. Exactly which element deserves focus and how the change should be announced to assistive technology is an accessibility question with its own rules; that the router must do *something* at commit is the routing fact. - **Title.** The title names the history entry in the back-button menu, identifies the tab, becomes the bookmark's name, and is typically the first thing a screen reader user hears about a new screen. It is therefore route-level data, updated on commit and ideally including the state the address encodes — a filtered view that shares a title with the unfiltered one makes a deep back stack unreadable. - **Ordering.** Commit, let the new content render, then restore scroll and move focus. Doing either before the content exists produces the clamped-offset bug for scroll and a lost focus target for keyboard users. ## What to be careful about - Do not reset to the top on a navigation the user perceives as staying put; that is the most reported complaint of the whole feature. - Do not restore over a position the user chose during a pending transition. - Do not treat restoration as cosmetic. Back that returns to a remembered position is how a list-and-detail app feels correct.

  • Why key saved scroll offsets by history entry rather than by address?
    Because the same address can occupy several entries in one session at different positions — a list visited, left, and returned to from two places. Keying by address makes those entries share one offset, so traversing between them restores the wrong position and overwrites the other's.
  • Why does restoring scroll immediately at commit often land the user in the wrong place?
    The offset is clamped to the height that exists at that instant. Immediately after commit the new content may not be rendered or measured, so a large offset collapses to the bottom of a short container. Restore once the content that establishes the height is present, and re-apply for content that arrives in pieces.
  • A filter change resets the page to the top and users complain. What is the policy error?
    A navigation that only refines the current location is being treated as a new destination. Refinements should leave the scroll position alone — and usually replace the history entry rather than push — because the user did not perceive themselves as going anywhere.
  • Why is the title part of the routing policy rather than a cosmetic detail?
    It labels the history entry, the tab and any bookmark, and it is usually the first thing announced about a new screen. A stale title tells the user they are somewhere they are not, and identical titles across states make a deep back stack impossible to read.

saying these in an interview costs you the question

  • The browser restores scroll for you in a client-rendered app
  • Store one scroll offset per address
  • Reset to the top on every navigation
  • Restore the offset immediately at commit
  • Focus needs no attention because the new screen is visible
  • The title only matters for search engines