Navigating to a new page puts you back at the top, and hitting back or forward drops you exactly where you were. That much is Vue Router's own scrollBehavior hook, which is the only place in the router that gets handed the saved browser position, so it is the right place for all of this to live. What the starter adds on top is a way to say "these routes are one screen, do not touch the scroll when moving between them".
The default of resetting to the top is correct almost everywhere, but it falls apart on tabbed sections. Account settings is the obvious case. The tabs sit at the top of a long form, you scroll down, you click across to another tab, and the reset yanks you back up even though the chrome around the tabs never moved. As far as the user is concerned they did not navigate anywhere, they just switched a tab, so the page jumping is jarring.
A scroll group says these routes are the same screen. Move between any two of them and the position is left alone. Move in or out of the group from anywhere else and the normal reset applies.
Put a scroll key in the route meta with a name of your choosing. Since meta is inherited, the natural place is the layout route that owns the tabs, and every child underneath picks it up.
{
path: 'account',
component: LayoutAccount,
meta: {
scroll: 'account'
},
children: [{
path: 'profile',
name: 'user-account-profile',
component: AccountProfile
}, {
path: 'security',
name: 'user-account-security',
component: AccountSecurity
}]
}
Moving from profile to security now holds the scroll. Moving from either one out to the dashboard resets it, since the dashboard has no group.
The value is a plain string and the only thing that matters is that grouped routes share it. Nested groups are fine too, a child route can declare its own scroll and the deepest match wins, which lets a subsection break out of its parent's group when it really is a separate screen.
The hook works through four checks in order. A saved position from a back or forward navigation wins outright, since restoring where the user was is always what they expect. Then a hash on the incoming route scrolls to that element. Then the group check, where a shared group or an unchanged path leaves the scroll untouched. Everything else falls through to the top.
The path check at the end covers query and hash-only changes on the same route, filters, pagination state, anything that rewrites the URL without really going anywhere.
Restoring a saved position on a lazily loaded view can land short if the content has not painted at the height it will settle at. It is a known Vue Router wrinkle rather than anything specific to the starter, and the fix when it bites is to return a promise from the hook that resolves once the view is up. The starter does not do this by default because the page transitions already delay the reveal enough that it rarely shows.
The whole thing assumes the window is what scrolls. If a layout ever moves its scrolling into an inner container, the hook needs to target that element instead of the window.
Page Transitions for the load-state tiers the scroll reset lands alongside. Layouts for where tabbed sections and their layout routes come from.