#web #navigation-api

[ fukuoka.ts #4 / 2026.05.07 ]

Navigation API.

// SPAに適した新しいルーティングAPI

@ezakichi3207 15min LT navigation
@ezakichi3207 / fukuoka.ts #4
QR scan to view slides
#self-intro

$ whoami

えざきち

name —

えざきち

role :: Product Engineer
org :: サイボウズ株式会社 / kintone開発
x :: @ezakichi3207
github :: shoken3207
site :: zakki-portfolio
最近、鳥取・岡山
旅行に行ってきました
@ezakichi3207 / fukuoka.ts #4
QR
#navigation-api

Navigation API、 使ってますか?

@ezakichi3207 / fukuoka.ts #4
QR
#navigation-api

ブラウザのナビゲーションアクションやアプリケーションの履歴を管理する機能を提供するAPIで、History APIの後継として設計されました。

Baseline 2026 — Chrome, Edge, Firefox, Safari 全対応

@ezakichi3207 / fukuoka.ts #4
QR
#history-api

Navigation APIは、SPAを前提とした、新しいルーティングAPI!

@ezakichi3207 / fukuoka.ts #4
QR
#spa-routing

SPA ルーティングの 仕組み

  • リンクをクリック → ブラウザ遷移を preventDefault() で止める
  • history.pushState() で URL だけ書き換える
    ページリロードは発生せず、アドレスバーだけ変わる
  • React が新しいコンポーネントをレンダリング
    URL に対応する画面を JS で描画する

つまり SPA は「ブラウザの遷移を乗っ取って JS で画面を差し替えている」。この仕組みの土台が History API

@ezakichi3207 / fukuoka.ts #4
QR
#spa-routing

ルーティングライブラリが History API を隠蔽している

  • React Router — <Link>, useNavigate()
    内部で history.pushState() + popstate リスナーを管理
  • Next.js — <Link>, next/router, next/navigation
    App Router / Pages Router ともに History API ベースのルーティング
  • Vue Router, SvelteKit なども同様
    各フレームワークが History API のラッパーを提供

普段意識しないが、裏側では全て History API が動いている。その History API 自体に課題がある

@ezakichi3207 / fukuoka.ts #4
QR
#history-api

History API は 何がダメだったのか?

@ezakichi3207 / fukuoka.ts #4
QR
#history-api

History API の 課題

  • 遷移の中断処理を自前で書かないといけない
  • ページ遷移のイベントが分散している
    リンククリックは click、ブラウザバックは popstateイベントが発火する。同じ「遷移」なのにバラバラ
  • 遷移前にインターセプトできない
    popstate は遷移後に発火するため、遷移をブロックするには history.go(1) で巻き戻すハックが必要
  • 遷移の「開始」と「完了」をブラウザが認識できない
    pushState は URL を書き換えるだけ。ブラウザはナビゲーション中であることを知らないため、ローディングインジケーターもスクロール位置の自動復元も機能しない
@ezakichi3207 / fukuoka.ts #4
QR
#navigation-api
  • 中断処理が自動
    event.signal が AbortSignal を提供。連続遷移で前の fetch が自動キャンセルされる
  • イベントが navigate 1つに統一
    リンククリック・pushState・ブラウザバック — すべて同じイベントでハンドリング
  • 遷移前にインターセプト可能
    navigate イベントで preventDefault() すれば遷移をブロックできる。popstate のような事後対応は不要
  • 遷移の開始/完了をブラウザが認識
    intercept() で Promise を渡せば、ブラウザがローディングインジケーターやスクロール復元を自動制御
@ezakichi3207 / fukuoka.ts #4
QR
#loading-indicator

ブラウザの ローディングインジケーターと連動する(SSR)

History API vs Navigation API — ローディングインジケーターの違い

@ezakichi3207 / fukuoka.ts #4
QR
#history-api

Navigation APIが便利なことはわかった

@ezakichi3207 / fukuoka.ts #4
QR
#history-api

ルーティングライブラリを使っていたらあまり関係なくない?

@ezakichi3207 / fukuoka.ts #4
QR
#key-message

ルーティング自体は フレームワークが吸収してくれる。

react-router や next/router・link が内部で Navigation API を活用するようになるだけ。ゴリゴリ書く機会は多くない

@ezakichi3207 / fukuoka.ts #4
QR
#key-message

ただし、ルーティングライブラリでは カバーしきれない領域がある。

ページ離脱防止、フォーカス復元、遷移アニメーション — これらは自前で実装する必要がある

@ezakichi3207 / fukuoka.ts #4
QR
#section

フレームワークでは 届かないところ

実践
@ezakichi3207 / fukuoka.ts #4
QR
#use-case

入力途中でのページ離脱 確認ダイアログを表示

入力途中でブラウザバックしたときの確認ダイアログ

入力途中で誤ってブラウザバックしてしまっても、確認ダイアログを表示して意図しないページ離脱を防ぐ

@ezakichi3207 / fukuoka.ts #4
QR
#code

入力途中でのページ離脱 確認ダイアログを表示

usePreventLeave.ts

useEffect(() => {
  const handler = (event: NavigateEvent) => {
    if (!hasUnsavedChanges) return;
    // SPA 内遷移でも確認ダイアログを表示できる
    const leave = window.confirm('変更が保存されていません。離れますか?');
    if (!leave) event.preventDefault();
  };
  navigation.addEventListener('navigate', handler);
  return () => navigation.removeEventListener('navigate', handler);
}, [hasUnsavedChanges]);
@ezakichi3207 / fukuoka.ts #4
QR
#demo

入力途中でのページ離脱 確認ダイアログを表示 — デモ

@ezakichi3207 / fukuoka.ts #4
QR
#focus-problem

ブラウザバックしたら 元のフォーカス位置を維持

Xの例:ポストを開いてブラウザバックしたとき、前のフォーカスが戻らない

フォーカスが消失してしまうと、再びtabキーでフォーカスをたどらないといけない

@ezakichi3207 / fukuoka.ts #4
QR
#code

ブラウザバックしたら 元のフォーカス位置を維持

useFocusRestore.ts

// 遷移前にフォーカス位置を state に保存
navigation.updateCurrentEntry({
  state: { focusedId: document.activeElement?.id },
});

// 戻ってきたら復元
navigation.addEventListener('currententrychange', () => {
  const state = navigation.currentEntry.getState();
  if (state?.focusedId) {
    document.getElementById(state.focusedId)?.focus();
  }
});

遷移前に updateCurrentEntry() でフォーカス中の要素IDを履歴エントリに保存し、戻ってきたら getState() で取り出してフォーカスを復元する

@ezakichi3207 / fukuoka.ts #4
QR
#demo

ブラウザバックしたら 元のフォーカス位置を維持 — デモ

@ezakichi3207 / fukuoka.ts #4
QR
#use-case

ページ間アニメーション

View Transitions API と Navigation API を組み合わせて、スムーズな遷移を SPAで実装

@ezakichi3207 / fukuoka.ts #4
QR
#code

ページ間アニメーション

router.ts

navigation.addEventListener('navigate', (event) => {
  if (!event.canIntercept) return;

  event.intercept({
    handler() {
      return document.startViewTransition(() => {
        updateContent(event.destination.url);
      }).finished;
    }
  });
});

navigate イベントでリンククリックもブラウザバックも一括ハンドリング。View Transitions と組み合わせるだけでページ遷移アニメーションが完成する

@ezakichi3207 / fukuoka.ts #4
QR
#demo

デモ

@ezakichi3207 / fukuoka.ts #4
QR
#tips

まとめ

  • ルーティング自体はフレームワークに任せてOK
    Navigation API を直接書く必要はほとんどない
  • ルーターがカバーしない UX 改善に使える
    離脱防止・フォーカス復元・遷移アニメーションは直接使う価値あり
  • Baseline 2026 — 今日から使える
    if ('navigation' in window) でフォールバックも容易
@ezakichi3207 / fukuoka.ts #4
QR
#outro

— fin

Thank you.

Navigation API を使ってみよう!

@ezakichi3207 15min LT navigation

logout

@ezakichi3207 / fukuoka.ts #4
QR scan to view slides