テック技報最新号へ →
コード

TanStack Router: 入れ子のスクロール領域もスクロール復元・先頭へ戻す

TanStack Router は既定では window だけを先頭に戻す。メインの領域が overflow: auto の div のようなレイアウトでは、scrollToTopSelectors にその要素を指定し、scrollRestoration: true で戻る操作のときに位置を復元させる。

@azu_re · 投稿 2026-03-30
#react#tanstack-router#frontend#scroll

何が困るか

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 /> コンポーネントは非推奨。ルーターのオプションで設定する。

出典

X の元の投稿を開く