UIXHERO

Loading(ローディング)

処理中であることをユーザーに伝えるUIパターン全体の設計指針。Spinner・Skeleton・Progress Barの使い分け・知覚的な待機時間の最適化・aria-busyとaria-liveの実装を解説する。

2026年3月3日
更新: 2026年9月11日
20
by Dengen Yosho(DGYS)

処理中・データ取得中であることをユーザーに伝えるパターン全体を指す設計概念。具体的な実装として Spinner(回転アニメーション)Skeleton(コンテンツ形状のプレースホルダー)Progress Bar(進捗バー) の3種類があり、それぞれ異なる用途に使われる。

この記事を読むと、3種類のローディングUIの使い分け・知覚的な待機時間の最適化(遅延表示・アニメーション速度)・aria-busyaria-live の正しい実装が自分でできるようになります。


1. UI例(Preview / Live)

実装で見る(GunjoUI)

この部品を、デザインシステム GUNJO の実装で確かめられます。

2. 定義(Definition)

処理中・データ取得中であることをユーザーに伝えるUIパターン全体。単体のコンポーネントではなく、Spinner・Skeleton・Progress Bar・オーバーレイ の4つの実装パターンをに応じて使い分ける設計概念。

3種類のローディングUIの使い分け

種類進捗計測形状保持使い所
Spinner短い不定時間の処理(送信・認証)
Skeletonコンテンツ一覧・カード・プロフィールの読み込み
ファイルアップロード・インストール・長い処理
オーバーレイ既存コンテンツを操作不可にしながら処理

3. 使い分け(When to use / When NOT to use)

3.1 When to use(種類別)

  • Spinner:送信ボタンの処理中・認証中・数秒以内の短い不定時間の待機
  • Skeleton:リスト・カード・フィード・テーブルの初回データ取得
  • Progress Bar:ファイルアップロード・ダウンロード・インストール・進捗が計測できる長い処理
  • オーバーレイ:フォーム送信中に背後のフォームを操作不能にしたい場合

3.2 When NOT to use

  • 100ms以下の処理:Spinnerが一瞬点滅して逆にUIが不安定に見える → 表示しない
  • ページ全体を覆うSpinner:Skeletonで形状を保てる場合は Skeleton の方がが良い
  • 進捗が計測できないのにProgress Bar:偽の進捗(フェイクプログレス)はユーザーの信頼を損なう

3.3 代替UI(Alternatives)

  • 処理完了後の結果 → Alert(成功/エラー)/ Toast
  • データが空の時 → Empty State
  • 個別コンポーネントの詳細 → SpinnerProgress Bar

4. 設計判断の核(Decision Principles)

Loadingの核は「待機時間の知覚最適化」——実際の処理時間は変えられなくても、表示方法次第でユーザーの体感待機時間を大幅に短くできる。

判断の優先順位:① 処理時間の分類(100ms / 1秒 / 10秒)→ ② 進捗計測可否 → ③ 形状保持可否 → ④ aria-busy 設定

  • 100ms以下はローディングを表示しない:表示と消去の点滅がかえってUIを不安定に見せる。setTimeout で100〜200ms させてからローディングを表示し、それ以前に処理が完了した場合は表示しない
  • Skeletonはコンテンツの形状を保つことで不安を減らす:真っ白なSpinnerより「こういうコンテンツが来る」というがあると体感待ち時間が短くなる。UIの形状が決まっているリスト・カード・プロフィールには必ずSkeletonを使う
  • aria-busy="true" を処理中の要素に付与する:スクリーンリーダーが aria-busy="true" の要素を読み上げる時に「読み込み中」と伝えてくれる。ボタンの場合は disabled + aria-busy="true" を組み合わせる
  • アニメーション速度は1〜2秒サイクルが最適:速すぎるSpinnerは焦りを与え、遅すぎると止まっているように見える。CSSの animation-duration: 0.8s〜1.2s が自然に感じられる範囲

5. 状態設計(States)

5.1 主な状態遷移

  • Idle(待機)Loading(処理中)Success / Error(完了)
  • Loading が長引く場合:LoadingTimeout / Retry(タイムアウト)

5.2 遅延表示のパターン

処理時間表示するもの
〜100ms何も表示しない
100ms〜1秒Skeleton または インラインSpinner
1秒〜10秒Spinner + テキスト(「読み込み中...」)
10秒以上Progress Bar + キャンセルボタン

6. バリエーション設計(Variants)

バリアント特徴使用例
インラインSpinnerボタン・入力欄内に小型Spinnerを埋め込む送信ボタン・検索フィールド
ページSpinnerページまたはセクション全体の中央に表示初期ページロード
Skeletonコンテンツ形状のグレープレースホルダーカード・リスト・プロフィール
Progress Bar進捗率を数値と棒グラフで表示ファイル操作・インストール
オーバーレイ半透明 + 中央Spinnerフォーム送信・保存処理

禁止パターン:進捗が計測できないのに 0% → 100% のフェイクプログレスを実装する → ユーザーが偽の進捗に気づいた時の信頼低下が大きい。不定時間の処理はSpinner / Skeletonを使う。


7. パターン集(Good / Bad / How to fix)

7.0 よく崩れる設計パターン(3つ)

  • 100ms以下の処理にSpinnerを表示:高速な操作のたびにSpinnerが一瞬点滅し、UIが不安定に見える
  • Skeletonの形状がコンテンツと全く異なる:Skeleton が単なる横線3本で、実際に表示されるのは複雑なカードグリッドという場合、コンテンツ出現時の「ずれ」がUXを損なう
  • ローディング完了後もスクリーンリーダーに伝えないaria-busy="false" に戻すか aria-live でコンテンツが更新されたことを伝えないと、スクリーンリーダーユーザーがコンテンツの読み込み完了を認識できない

7.1 Bad(典型3つ)

  • onClick={() => setLoading(true)} を即時実行し、100ms以下で完了する処理にもSpinnerが表示される
  • ページ全体に position: fixed のSpinnerオーバーレイを表示し、実際のコンテンツ形状が全く見えない
  • 進捗が計測できないAPI呼び出しに Progress Bar を使い、0%→30%→30%(止まる)→100% のような偽の進捗を表示

7.2 Good(対になる3つ)

  • useEffectsetTimeout(200ms) の遅延タイマーを設定し、200ms以内に処理が完了した場合はSpinnerを表示しない(点滅防止)
  • Skeletonの各プレースホルダーを実際のコンテンツの形状(アバター円・タイトル行・説明文行)に合わせてデザインし、コンテンツ出現時のレイアウトシフトを最小化する
  • コンテンツ読み込み完了後に aria-busy="false" に戻し、読み込んだコンテンツのコンテナに aria-live="polite" を付与して更新を通知する

7.3 How to fix(手順)

  1. 処理時間を計測し100ms / 1秒 / 10秒で分類する
  2. useState + useEffect で遅延表示を実装:const [showLoading, setShowLoading] = useState(false)useEffect(() => { const t = setTimeout(() => setShowLoading(true), 200); return () => clearTimeout(t); }, [isLoading])
  3. aria-busy={isLoading} を処理中の要素に付与する
  4. Skeletonを使う場合は実際のコンテンツレイアウトに合わせて形状を設計する

8. ルール(Must / Better)

Must(守らないと壊れる)

Better(品質が跳ねる)


9. アクセシビリティ要件(必須)

Screen Reader

<!-- ボタンのローディング -->
<button disabled aria-busy="true">
  <span aria-hidden="true">⟳</span>
  処理中...
</button>

<!-- セクションのローディング -->
<div aria-busy="true" aria-label="コンテンツを読み込み中">
  <!-- Skeleton または Spinner -->
</div>

<!-- ページレベルのローディング -->
<main aria-busy="true">
  <!-- コンテンツ -->
</main>

Contrast / Readability

  • Spinnerの色と背景のは3:1以上(大きな非テキスト要素の基準)
  • prefers-reduced-motion メディアクエリで animation: none にしてを無効化するオプションを提供する
@media (prefers-reduced-motion: reduce) {
  .spinner {
    animation: none;
    opacity: 0.5;
  }
}

10. 実装メモ(Implementation Notes)

  • shadcn/ui には Skeleton コンポーネントがある(animate-pulse を使った実装)。Spinnerは shadcn/ui にないため、Tailwind の animate-spin + border-t-[color] で実装するか、lucide-reactLoader2 アイコンを使う
  • 遅延表示の実装は useDeferredValue(React 18)または setTimeout + useEffect で行う。useDeferredValue は Concurrent Mode での非同期レンダリングと組み合わせる際に有効
  • prefers-reduced-motion の対応は Tailwind の motion-reduce: プレフィックスで実装できる:className="animate-spin motion-reduce:animate-none"

11. 関連リンク


12. まとめ

Loadingの設計で最重要なのは「処理時間の分類と適切なUIパターンの選択」です。迷ったら 4. 設計判断の核 に戻り、「100ms以下は非表示か」「進捗が計測できるかどうかでSpinner/Progress Barを選んでいるか」「コンテンツ形状が決まっているならSkeletonを使っているか」の3点を確認してください。aria-busy="true" の付与はアクセシビリティの最低ラインとして必ず実装してください。

更新のお知らせ

サイトに載せていない実例や、新しい記事のお知らせはこちらで出しています。

読んだ内容を、自分の画面に当てるとき

UIXHEROは、記事を書くほかに、画面の検品・判定、デザインシステムの構築、実装と改善の伴走を受けています。何を頼めばいいか決まっていない段階の相談も、同じ窓口で受けます。

UIXHEROに頼めることを見る

※ 記事の内容についての質問や、書いてほしいテーマの要望も同じ窓口で受けています

記事をシェア

メールでお知らせを受け取る

最終更新: 2026年9月11日

この記事を書いた人

Dengen Yosho(DGYS)

Dengen Yosho(DGYS)

UI/UXデザイナー兼デザインエンジニア デザイン歴25年以上 情報設計〜UIデザイン〜React/TypeScriptによる実装までを一気通貫で担当 大手企業のDXプロジェクトや決済アプリ領域で、デザインリードとして改善と構築を経験 現在はフリーランスとして、プロダクトのUI/UX改善とデザインシステム構築を支援 長年の実務の中で感じてきたのは、 「良いUI」はセンスではなく、再現可能な判断の積み重ねであるということ UIXHEROは、 心理学や行動科学の知見を、現場で使える設計言語に翻訳するための場所 関心領域は「ユーザーの行動が変わるUI」 人の認知・注意・意思決定の構造を理解し、 “なぜそのUIが機能するのか”を言語化することを目指している

あわせて読みたい

この記事に関連する記事をご紹介します

Day Band(いちにちの帯)

1日を1本の帯にして、時間帯を面で、節目を印で、いまの時刻とつまみを重ねて見せるUIコンポーネント。器が1日に固定されることの意味、4つの層の重ね方、印のラベルと時刻の数字を分ける理由、触った瞬間に動かさない理由を解説する。

2026年9月14日
24

Live Badge(LIVEの札)

隣の数字がいまの値かどうかを示す小さな札。状態を表す語を枠で囲って独立させる理由、明滅と色を補助に留める理由、実時間から外れたときに「古い」ではなく「いつの値か」を出す設計判断とARIA実装を解説する。

2026年9月14日
21

Time Transport(時間の操作盤)

いま映している時刻を、名前をつけた速さの段で動かし、決まった幅で飛ばし、ひと押しで「いま」へ戻すための操作盤。速さを連続値にしない理由、向きをアイコンに持たせる理由、状態を大きい表示の色で示さない理由を解説する。

2026年9月14日
25

もっと深く知りたいですか?

ここに掲載されていないトピックについても、リクエストがあれば解説記事を追加します。 わかりにくい点や、具体的な事例について知りたいことがあれば教えてください。

リクエストを送る