From 212859d8da9c40618e2ced93d42060480bdeb817 Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Mon, 6 Oct 2025 11:28:49 -0700 Subject: [PATCH] fix(router): Scroll restoration should use instant scroll behavior for traversals When the scroll position is being restored, this change upates the behavior to use 'instant' rather than the default 'auto', which will be whatever the browser behavior is for 'window.scrollTo'. The 'smooth' behavior does not match how browsers behavior when performing a traversal navigation for MPAs, which is 'instant'. related to #58258 --- packages/router/src/router_scroller.ts | 5 +++-- packages/router/test/router_scroller.spec.ts | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/router/src/router_scroller.ts b/packages/router/src/router_scroller.ts index 9e14bd0b3576..739c0e39b545 100644 --- a/packages/router/src/router_scroller.ts +++ b/packages/router/src/router_scroller.ts @@ -85,12 +85,13 @@ export class RouterScroller implements OnDestroy { private consumeScrollEvents() { return this.transitions.events.subscribe((e) => { if (!(e instanceof Scroll)) return; + const instantScroll: ScrollOptions = {behavior: 'instant'}; // a popstate event. The pop state event will always ignore anchor scrolling. if (e.position) { if (this.options.scrollPositionRestoration === 'top') { - this.viewportScroller.scrollToPosition([0, 0]); + this.viewportScroller.scrollToPosition([0, 0], instantScroll); } else if (this.options.scrollPositionRestoration === 'enabled') { - this.viewportScroller.scrollToPosition(e.position); + this.viewportScroller.scrollToPosition(e.position, instantScroll); } // imperative navigation "forward" } else { diff --git a/packages/router/test/router_scroller.spec.ts b/packages/router/test/router_scroller.spec.ts index 0176037e3a2b..90d0e7bd6971 100644 --- a/packages/router/test/router_scroller.spec.ts +++ b/packages/router/test/router_scroller.spec.ts @@ -96,7 +96,9 @@ describe('RouterScroller', () => { events.next(new NavigationStart(3, '/a', 'popstate', {navigationId: 1})); events.next(new NavigationEnd(3, '/a', '/a')); await nextScrollEvent(events); - expect(viewportScroller.scrollToPosition).toHaveBeenCalledWith([10, 100]); + expect(viewportScroller.scrollToPosition).toHaveBeenCalledWith([10, 100], { + behavior: 'instant', + }); }); }); @@ -146,7 +148,7 @@ describe('RouterScroller', () => { events.next(new NavigationEnd(3, '/a#anchor', '/a#anchor')); await nextScrollEvent(events); expect(viewportScroller.scrollToAnchor).not.toHaveBeenCalled(); - expect(viewportScroller.scrollToPosition).toHaveBeenCalledWith([0, 0]); + expect(viewportScroller.scrollToPosition).toHaveBeenCalledWith([0, 0], {behavior: 'instant'}); }); });