TanStack Router: 入れ子のスクロール領域もスクロール復元・先頭へ戻す
TanStack Router は既定では window だけを先頭に戻す。メインの領域が overflow: auto の div のようなレイアウトでは、scrollToTopSelectors にその要素を指定し、scrollRestoration: true で戻る操作のときに位置を復元させる。
何が困るか
SPA は history.pushState で遷移するので、ブラウザが自動でスクロール位置を戻してくれない。さらにサイドバー+メイン領域のように、スクロールするのが body ではなく入れ子の div だと、遷移しても前のページのスクロール位置のまま表示される。
やり方
tsx
const router = createRouter({
routeTree,
// 戻る・進むでスクロール位置を復元(window と各スクロール領域を記録)
scrollRestoration: true,
// 新しいページへ遷移したとき、window に加えてこの要素も先頭へ
scrollToTopSelectors: ['#main-scrollable-area'],
// スムーズスクロールをやめて即座に移動
scrollRestorationBehavior: 'instant',
})scrollToTopSelectorsには、document.querySelectorで取れないもの(Shadow DOM の中など)のために、要素を返す関数も渡せる。- パスごとに同じ位置を使いたいときは
getScrollRestorationKey: (location) => location.pathname。既定は履歴エントリごとのキー(location.state.__TSR_key)。 - ある遷移だけスクロールを触らせたくないときは
navigate({ resetScroll: false })や<Link resetScroll={false}>。 - 仮想リストは自動復元が効かないので、
useElementScrollRestorationで保存されたscrollYを取り、仮想化ライブラリのinitialOffsetに渡す。要素にはdata-scroll-restoration-idを付ける。
注意点
- window を対象から外すことは今のところできない(指定したセレクタは window に追加して処理される)。
- 旧版(v1.121.34 より前)は既定キーが
state.key。こちらは非推奨。 - 以前の
<ScrollRestoration />コンポーネントは非推奨。ルーターのオプションで設定する。
出典
- @azu_re · 2026-03-30
- TanStack Router 公式ドキュメント「Scroll Restoration」