From bc0ece8eff5b722d3a0c14c4969c241c6679ee1d Mon Sep 17 00:00:00 2001
From: Nic Polumeyv <162764842+Nic-Polumeyv@users.noreply.github.com>
Date: Sat, 18 Jul 2026 14:01:16 -0400
Subject: [PATCH 1/3] docs: add scroll management page
---
.../docs/30-advanced/66-scroll-management.md | 41 +++++++++++++++++++
1 file changed, 41 insertions(+)
create mode 100644 documentation/docs/30-advanced/66-scroll-management.md
diff --git a/documentation/docs/30-advanced/66-scroll-management.md b/documentation/docs/30-advanced/66-scroll-management.md
new file mode 100644
index 000000000000..5b6c6c146074
--- /dev/null
+++ b/documentation/docs/30-advanced/66-scroll-management.md
@@ -0,0 +1,41 @@
+---
+title: Scroll management
+---
+
+When you navigate between pages, SvelteKit mirrors the browser's default behaviour: it scrolls to the top of the page (or to the element with a matching ID, if the link includes a `#hash`), and restores the previous scroll position when you navigate back or forward. You can opt out per link with [`data-sveltekit-noscroll`](link-options#data-sveltekit-noscroll) or per navigation with the `noScroll` option of [`goto`]($app-navigation#goto).
+
+This applies to the scroll position of the _document_. If your pages scroll inside a custom container instead — for example a layout where `` and `
` have `overflow: hidden` and an inner element scrolls, as is common in mobile-style app shells — SvelteKit has no way of knowing which element it should scroll. The container will keep its scroll position when you navigate, and nothing will be restored when you go back.
+
+Since only your app knows which element scrolls, you can manage the container yourself with [`afterNavigate`]($app-navigation#afterNavigate) and a [snapshot](snapshots) in the layout that owns it:
+
+```svelte
+
+
+
+
+ {@render children()}
+
+```
+
+On every navigation, `capture` saves the container's scroll position against the outgoing history entry immediately before the page updates, and `afterNavigate` scrolls new pages to the top. On back and forward navigations, `restore` is called with the saved position after the `afterNavigate` callbacks have run, so the two don't conflict. Snapshots are persisted to `sessionStorage`, so the position also survives a reload.
+
+> [!NOTE] Passing [`behavior: 'instant'`](https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollTo) makes the reset immediate even if the container has [`scroll-behavior: smooth`](https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-behavior) in CSS, which would otherwise animate it on every navigation.
From 8f1df9904dc39ac86cbb2f88cb81daa130d9ec72 Mon Sep 17 00:00:00 2001
From: Nic Polumeyv <162764842+Nic-Polumeyv@users.noreply.github.com>
Date: Sat, 18 Jul 2026 14:09:31 -0400
Subject: [PATCH 2/3] docs: cover hash links and noScroll interplay
---
documentation/docs/30-advanced/66-scroll-management.md | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/documentation/docs/30-advanced/66-scroll-management.md b/documentation/docs/30-advanced/66-scroll-management.md
index 5b6c6c146074..33a73c4412fe 100644
--- a/documentation/docs/30-advanced/66-scroll-management.md
+++ b/documentation/docs/30-advanced/66-scroll-management.md
@@ -36,6 +36,8 @@ Since only your app knows which element scrolls, you can manage the container yo
```
-On every navigation, `capture` saves the container's scroll position against the outgoing history entry immediately before the page updates, and `afterNavigate` scrolls new pages to the top. On back and forward navigations, `restore` is called with the saved position after the `afterNavigate` callbacks have run, so the two don't conflict. Snapshots are persisted to `sessionStorage`, so the position also survives a reload.
+On every navigation, `capture` saves the container's scroll position against the outgoing history entry immediately before the page updates, and `afterNavigate` scrolls new pages to the top. On back and forward navigations, `restore` is called with the saved position after the `afterNavigate` callbacks have run, so the two don't conflict. Snapshots are persisted to `sessionStorage`, so the position also survives a reload. Links with a `#hash` keep working, since SvelteKit scrolls the targeted element into view within its container after the reset.
+
+Note that `afterNavigate` callbacks can't tell whether a navigation used [`data-sveltekit-noscroll`](link-options#data-sveltekit-noscroll) or `goto`'s `noScroll` option, so the container is also reset on those navigations.
> [!NOTE] Passing [`behavior: 'instant'`](https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollTo) makes the reset immediate even if the container has [`scroll-behavior: smooth`](https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-behavior) in CSS, which would otherwise animate it on every navigation.
From f1f9105b414c3f7ad6824f1699b33a37939c1d9b Mon Sep 17 00:00:00 2001
From: Nic Polumeyv <162764842+Nic-Polumeyv@users.noreply.github.com>
Date: Sat, 18 Jul 2026 14:16:13 -0400
Subject: [PATCH 3/3] docs: drop incorrect ordering claim for hash links
---
documentation/docs/30-advanced/66-scroll-management.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/documentation/docs/30-advanced/66-scroll-management.md b/documentation/docs/30-advanced/66-scroll-management.md
index 33a73c4412fe..dc026d1efdb0 100644
--- a/documentation/docs/30-advanced/66-scroll-management.md
+++ b/documentation/docs/30-advanced/66-scroll-management.md
@@ -36,7 +36,7 @@ Since only your app knows which element scrolls, you can manage the container yo
```
-On every navigation, `capture` saves the container's scroll position against the outgoing history entry immediately before the page updates, and `afterNavigate` scrolls new pages to the top. On back and forward navigations, `restore` is called with the saved position after the `afterNavigate` callbacks have run, so the two don't conflict. Snapshots are persisted to `sessionStorage`, so the position also survives a reload. Links with a `#hash` keep working, since SvelteKit scrolls the targeted element into view within its container after the reset.
+On every navigation, `capture` saves the container's scroll position against the outgoing history entry immediately before the page updates, and `afterNavigate` scrolls new pages to the top. On back and forward navigations, `restore` is called with the saved position after the `afterNavigate` callbacks have run, so the two don't conflict. Snapshots are persisted to `sessionStorage`, so the position also survives a reload. Links with a `#hash` still end up at the targeted element.
Note that `afterNavigate` callbacks can't tell whether a navigation used [`data-sveltekit-noscroll`](link-options#data-sveltekit-noscroll) or `goto`'s `noScroll` option, so the container is also reset on those navigations.