Next.js×microCMS|ブラウザバックや戻るボタンで元のスクロール位置に戻る方法

記事一覧を無限スクロールにしたけれど、記事から一覧に戻ったらスクロールがTOPに戻るのを解決

4/21/2025

🌀無限スクロール便利だと思ったら、見落としていた不便さが

無限スクロールで記事をたくさん一覧表示できるので、スムーズに知りたい情報に辿り着けそうと設置直後はワクワクしました。

しかし実際使ってみたところ、すごく不便な部分が発覚。

一覧ページから子ページ(記事詳細ページ)に飛んで、内容を確認してやっぱり違う記事を読もうと思いブラウザバックすると、記事一覧のトップページに出戻ってしまった…⚡️

これではスクロールで記事をたくさん探せても、読むたびに振り出しに戻る「無限スクロール地獄」になってしまう。

✅スクロールY位置をセッション保存して解決

目的は 「現在のページから子ページへ遷移 → 戻ってきたときのみスクロール位置を復元」 を実現すること。
まずはカスタムフックを作成していく(hooks/useScrollRestoration.jsx)。

useRouter で現在のページのルート情報を取得

const router = useRouter();

これにより、現在のパス(pathname)などの情報にアクセスでき、ルーティングイベントにも対応可能に。

② 親ページでマウント時にスクロール位置を保存する処理を設置

useEffect(() => {
  const handleRouteChangeStart = () => {
    const currentY = window.scrollY;
    sessionStorage.setItem(key, currentY);
  };

  router.events.on("routeChangeStart", handleRouteChangeStart);
  return () => {
    router.events.off("routeChangeStart", handleRouteChangeStart);
  };
}, [router, key]);
  • router.events.onrouteChangeStart イベントでページ遷移直前に実行されるように設定。
  • router.events.offrouteChangeStart イベントのクリーンアップ。
  • window.scrollY で現在の縦スクロール位置を取得し、sessionStorage に保存。

③ ページがマウントされたときにスクロール位置を復元

useEffect(() => {
  if (!isReady) return;
  const savedY = sessionStorage.getItem(key);
  if (savedY !== null) {
    const y = parseInt(savedY, 10);
    if (!isNaN(y)) {
      window.scrollTo(0, y);
      sessionStorage.removeItem(key); // ✅ 一度復元したら消す
    }
  }
}, [key, isReady]);
  • sessionStorage に保存されているスクロール位置を取得し、window.scrollTo で復元。
  • 一度復元したら、値を即座に削除することで リロードや他のページ遷移で再度適用されるのを防ぐ

🎉 結果

  • 「親ページ → 子ページ → 戻る」というルートでのみスクロール位置が復元される。
  • 「他ページに行く、リロードする、URL直打ちする」 などでは復元されず、初期状態に。

これで安心して記事を読んだら一覧ページに戻ってくることが可能に🙌

⚡️元のスクロール値に戻った時に、「Loading」UIが一瞬見える…?

一難去ってまた一難…予期せぬことばかり。

子ページから元のページに戻った時、一瞬「Roading」と表示されてその後に記事一覧が表示される。これはスクロール値に合わせてデータ取得させている「無限スクロール」の処理が再度走っているということ。

一瞬なのであまり気にならないかもしれないけれど、一度取得したデータをもう一度取得するのは手間に感じます。

対応する方法は次の記事に。

zustandを使用して無限スクロールで取得済みデータの再取得をしないようにする