0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

NextJS + App Router + RSC の採用=ローディング UX 設計もセット

Posted at

NextJSのページ遷移UX問題と解決策

😵 問題:ユーザーが混乱するページ遷移

  • NextJS App Router + RSCではページ遷移時の視覚的フィードバックが不足
  • ユーザーにとって以下が分かりづらい:
    • ページが遷移中なのか
    • データをロード中なのか
    • それとも単に何も起きていないのか

以下はブラウザのリロード中の図です。これによりユーザーは「何かが起きている」と思えるのですが、RSCを使用しているとこのようなリロードが発生しません。

ブラウザのリロード中の図

🔍 原因:RSCのフェッチ方式

  • App Router + RSCの場合、ページ遷移が通常のHTMLリロードではなくfetchで行われる
  • パフォーマンス向上のため、変更箇所のみを最適化して取得する仕組み
  • ブラウザの開発者ツールで確認すると /?_rsc=***** というリクエストが発生

RSCフェッチリクエスト

RSCリクエストの仕組み

項目 説明
?_rsc= の正体 React Server Components用のストリーミングデータフェッチ用内部API
発生タイミング App Router (/appディレクトリ)でServer Componentsを使用時
用途 サーバーコンポーネントのデータストリームをクライアントに送信
特徴 完全なページリロードなしで差分のみ更新

Next.js App Router + RSCのページ遷移では、従来のような「完全なリロード」がないため、ユーザーにとって視覚的なフィードバックが不足し、体験が悪化しがちです。

🎯 問題の本質

要因 詳細
fetch()ベースの遷移 ブラウザのローディングインジケーターが表示されない
差分更新の仕組み ページ全体ではなく変更部分だけ更新されるため変化が分かりにくい
視覚フィードバック不足 特にサーバー処理が遅い場合、ページが固まったように見える
ユーザー誤解 「遅延=バグ」と誤解され、離脱につながりやすい

✅ 解決策:3段階のローディングUI設計

1. ページ全体のローディング表示(loading.tsx

該当フォルダにloading.tsxを配置するだけで、遷移中の全体ローディングUIを設定できます。

// app/site/detail/loading.tsx
export default function Loading() {
  return (
    <div className="p-8 text-center">
      <p>読み込み中です…</p>
    </div>
  );
}

2. プログラムによる遷移時の状態表示(useTransition

ボタンクリックなどでページ遷移させる場合は、明示的に遷移中状態を管理します。

'use client';
import { useTransition } from 'react';
import { useRouter } from 'next/navigation';

export function NavigateButton({ id }) {
  const router = useRouter();
  const [isPending, startTransition] = useTransition();

  const handleClick = () => {
    startTransition(() => {
      router.push(`/site/detail/${id}`);
    });
  };

  return (
    <button onClick={handleClick} disabled={isPending}>
      {isPending ? '読み込み中...' : '詳細を見る'}
    </button>
  );
}

3. 部分的なローディング表示(Suspense

ページ内の特定コンテンツだけを非同期で表示する場合に使用します。

// app/site/detail/[id]/page.tsx
import { Suspense } from 'react';
import { DetailContent } from './DetailContent';
import { Skeleton } from '@/components/ui/Skeleton';

export default function DetailPage({ params }) {
  return (
    <Suspense fallback={<Skeleton />}>
      <DetailContent id={params.id} />
    </Suspense>
  );
}

💡 重要なポイント

ローディング種別 実装方法 適用場面
グローバルローディング loading.tsx ページ全体の遷移時
コンポーネント単位 <Suspense> ページ内の一部だけ非同期読み込み時
アクション単位 useTransition ボタンクリックなどの操作に紐づく遷移時

📝 結論:RSC採用=ローディングUX設計は必須

App Router + RSCの高速な差分更新の仕組みは、適切なローディングUIがセットになって初めて真価を発揮します

ユーザー視点では:

  • ✅ 何が起きているかが常に分かる
  • ✅ システムが反応していることが視覚的に確認できる
  • ✅ 離脱防止につながる

Next.js App Router + RSC の採用 = ローディングUX設計もセットで考える。これはもはや必須の設計原則です。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?