ユーザーのローカルファイルをシステムに取り込むコンポーネント。シンプルなボタン1つから始まるが、ドラッグ&ドロップ・進捗表示・エラーハンドリング・複数ファイル管理まで、状態が複雑に絡み合う高難度のUI。
この記事を読むと、ファイル選択UIの形式選択・アップロード状態設計・エラー種別の表現・A11y要件が自分でできるようになります。
1. UI例(Preview / Live)
実装で見る(GunjoUI)
この部品を、デザインシステム GUNJO の実装で確かめられます。
2. 定義(Definition)
ユーザーのローカルファイルをシステムに送信するための複合コンポーネント。ファイル選択(クリックまたはドラッグ&ドロップ)・バリデーション(形式・サイズ)・進捗表示・エラー表示・ファイル管理(追加・削除)を統合する。
単純な <input type="file"> との違い:HTMLネイティブの <input type="file"> は最低限の機能のみ。File Upload コンポーネントは進捗・エラー・ドラッグ&ドロップ・プレビューなどをUIとして統合したもの。
3. 使い分け(When to use / When NOT to use)
3.1 When to use
- ユーザーがローカルファイルをシステムにアップロードする必要がある場面(プロフィール画像・添付ファイル・書類提出など)
- 複数ファイルの一括アップロードや、進捗をリアルタイムで確認させたい場面
- ファイル形式・サイズの制限があり、即時バリデーションが必要な場面
3.2 When NOT to use
- テキスト入力で代替できる場面(URLや文字列を入力させる場合)
- アップロードが不要でファイルの選択結果だけが必要な場面(
<input type="file">で十分)
3.3 代替UI(Alternatives)
- シンプルなファイル選択のみ →
<input type="file">ネイティブ - URL入力でのメディア追加 →
Text Input - カメラ撮影でのアップロード →
<input type="file" capture="environment">
4. 設計判断の核(Decision Principles)
アップロードの「今」をユーザーに常に見せる——選択・検証・送信・完了・エラーの全ての状態が明示されていること。
判断の優先順位:① 状態の可視化 → ② エラー種別の明示 → ③ 制約条件の事前告知 → ④ 復旧手順の提示
- ファイルを選択した瞬間にファイル名とサイズを表示する(選択できたことの即時フィードバック)
- 制約(対応形式・最大サイズ・最大ファイル数)はアップロードゾーンに事前に明示する。エラーが起きてから知らせるのではなく、選択前に知らせる
- エラーは「何が問題か」と「どう直すか」をセットで表示する(「エラー」だけでは不十分)
- 進捗バーは「アップロード中」状態を示すだけでなく、完了・失敗でも状態を更新し続ける
5. 状態設計(States)
5.1 必須状態(Required)
- Idle(待機):ファイルが選択されていない初期状態。制約条件と操作方法を表示
- Selected:ファイルが選択された直後。ファイル名・サイズを表示し、バリデーション結果を即時フィードバック
- Uploading:APIへの送信中。進捗バー(または不確定スピナー)で進行を示す
- Done:アップロード完了。成功アイコンと確認メッセージを表示
- Error:アップロード失敗またはバリデーション失敗。エラー理由と再試行手段を明示
5.2 条件付き状態(Conditional)
- Dragging Over:ドラッグ&ドロップ操作中にゾーンがハイライトされる
- Disabled:条件を満たすまでアップロード不可(最大ファイル数に達したなど)
- Partial Error:複数ファイルの一部が成功・一部が失敗した混在状態
5.3 State Gallery
| 状態 | 必須 | 何を伝えるか |
|---|---|---|
| Idle | ✅ | 操作方法と制約(形式・サイズ)を事前告知 |
| Selected | ✅ | 選択できたこと・バリデーション結果の即時フィードバック |
| Uploading | ✅ | 今送信中であること・どれくらい進んだか |
| Done | ✅ | 正常に完了したこと |
| Error | ✅ | 何が問題で・どう修正すれば良いか |
| Dragging Over | — | ドロップできる場所であることを強調する |
| Disabled | — | なぜアップロードできないかを伝える |
6. バリエーション設計(Variants)
File Upload の形式は「ユーザーの操作コンテキスト」で選ぶ。
| バリアント | 目的 | 適した場面 |
|---|---|---|
| Dropzone | ドラッグ&ドロップ対応の大きなゾーン | 複数ファイル・大きなファイルの意識的なアップロード |
| Button only | クリックで選択するシンプルな形式 | フォーム内の1項目として自然に溶け込む場合 |
| With Preview | 選択後に画像サムネイルを表示 | 画像アップロード(プロフィール・商品画像など) |
| Multi-file list | 複数ファイルをリスト管理 | 書類提出・複数添付ファイル |
禁止パターン:選択後に何も表示しない(「ファイルが選択されました」の確認なし) → アップロードできたのか否かが不明。
7. パターン集(Good / Bad / How to fix)
7.0 よく崩れる設計パターン(3つ)
- 制約の後出し:ファイルを選択してアップロードボタンを押した後にはじめて「5MB以上は不可」と表示される → ユーザーは選ぶ前に知りたかった
- 進捗の非表示:アップロード中にローダーも進捗バーもなく、UIが固まったように見える → ユーザーは「壊れた?」と思い再クリックする
- エラーの曖昧さ:「アップロードに失敗しました」だけでは、サイズ問題か形式問題かネットワーク問題か判断できない
7.1 Bad(典型3つ)
- 対応形式と最大サイズをDropzoneに書かず、制限外ファイルを選択した後にエラーを出す
- アップロード中に進捗表示がなく、ボタンも押せたままで重複送信が発生する
<input type="file">のネイティブUIだけで完結させ、選択後のフィードバックがゼロ
7.2 Good(対になる3つ)
- Dropzone に「対応形式: JPEG, PNG, PDF(最大5MB)」を常時表示し、選択前に制約を伝える
- アップロード開始と同時にプログレスバーを表示し、完了・エラーで状態を更新する
- エラー時は「PDFのみ対応しています」「ファイルサイズが5MBを超えています。圧縮して再度お試しください」と原因と対処をセットで表示する
7.3 How to fix(手順)
- Dropzone / Button の近くに制約条件(形式・サイズ・件数)を書く
- ファイル選択直後にクライアントサイドバリデーションを実行し、エラーを即時表示する
- アップロード開始と同時に進捗UI(バーまたはスピナー)を表示し、ボタンを disabled にして重複送信を防ぐ
- 完了・エラーで状態を更新し、エラー時は再試行ボタンを提供する
8. ルール(Must / Better)
Must(守らないと壊れる)
Better(品質が跳ねる)
9. アクセシビリティ要件(必須)
Keyboard
- Dropzone はキーボードでフォーカスでき、
EnterまたはSpaceでファイル選択ダイアログを開けること - 各ファイルの削除ボタンは
Tabでフォーカスでき、Enter/Spaceで削除できること
Focus
- Dropzone・削除ボタンともにフォーカスリングを確保すること
Screen Reader
- Dropzone に
role="button"とaria-label="ファイルをドラッグ&ドロップ、またはクリックして選択"を付与する - 進捗バーに
role="progressbar"+aria-valuenow+aria-valuetextを付与する - エラーメッセージには
role="alert"を付与してスクリーンリーダーに即時通知する <input type="file">自体にはaria-labelを付与する
<!-- 基本パターン -->
<div
role="button"
tabindex="0"
aria-label="ファイルをドラッグ&ドロップ、またはクリックして選択"
>
<!-- Dropzone コンテンツ -->
</div>
<input type="file" aria-label="ファイルを選択" class="sr-only" />
<!-- 進捗バー -->
<div
role="progressbar"
aria-valuenow="45"
aria-valuemin="0"
aria-valuemax="100"
aria-label="report.pdf のアップロード進捗"
></div>
<!-- エラーメッセージ -->
<p role="alert">ファイルサイズが5MBを超えています。圧縮して再度お試しください。</p>
Touch / Pointer
- 削除ボタンのタップ領域を44×44px以上確保する
- モバイルではドラッグ&ドロップが難しいため、ボタンタップでのファイル選択(カメラロールなど)を必ず提供する
Contrast / Readability
- エラーテキストは背景色と4.5:1以上のコントラスト比を確保する
- Dropzone のボーダーと背景は3:1以上のコントラスト比(WCAG 1.4.11)
10. 実装メモ(Implementation Notes)
- ドラッグ&ドロップは
onDragOver(e.preventDefault()必須)/onDropイベントで実装する。e.preventDefault()を忘れるとブラウザがファイルを直接開いてしまう <input type="file">のvalueはリセットできないため、同じファイルを再選択させたい場合はonChange後にe.target.value = ''で値をクリアする- 大容量ファイルのアップロードは XMLHttpRequest(
xhr.upload.onprogress)またはライブラリ(axios)を使うと進捗が取得できる。fetchAPI は標準では進捗を取得できない - Radix UI / shadcn には標準の File Upload コンポーネントがないため、
react-dropzoneライブラリが最もメンテナンスされた選択肢
11. 関連リンク
- 関連するUIデザイン原則: フィードバック (Feedback), 明瞭性と確実性 (Clarity & Certainty), エラーから回復できる (Error Recovery), 誤操作を防ぐ (Error Prevention)
- 用語集(定義): アクセシビリティ (Accessibility), フィードバック (Feedback)
- 関連するUIコンポーネント(横): Button(ボタン), Toast(トースト通知), Dialog(ダイアログ / モーダル)
12. まとめ
File Upload の設計は「今どの状態にあるかを常にユーザーに見せる」ことに尽きます。迷ったら 4. 設計判断の核 に戻り、制約の事前告知・選択直後のフィードバック・進捗表示・エラーの明示の4点を確認してください。ネイティブの <input type="file"> はスタートラインに過ぎません——状態管理とフィードバックを統合して初めて「使えるFile Upload」になります。