ユーザーが指定した条件(カテゴリ・日付・価格帯・ステータスなど)でコンテンツを絞り込むUIコンポーネント。チェックボックス・ドロップダウン・トグルボタン・レンジスライダーなど複数の実装形式があり、「条件の複雑さ」と「常時表示か折りたたみか」の判断が設計の核。
この記事を読むと、フィルター形式の使い分け(チェックボックス vs ドロップダウン)・アクティブフィルターの可視化・一括クリア・URLへの状態永続化・フィルター数が多い場合のサイドバー vs モーダル設計が自分でできるようになります。
1. UI例(Preview / Live)
実装で見る(GunjoUI)
この部品を、デザインシステム GUNJO の実装で確かめられます。
2. 定義(Definition)
ユーザーが指定した条件セットでコンテンツを絞り込むUIコンポーネント。チェックボックス・トグルチップ・ドロップダウン・日付ピッカー・レンジスライダーなど複数の形式があり、条件の種類・選択肢の数・常時表示か折りたたみかによって最適な形式が変わる。
SearchとFilterの違い:Searchは「キーワードで探す(テキスト入力)」。Filterは「条件で絞る(選択)」。商品一覧では検索ボックス(Search)と価格帯・カテゴリの絞り込み(Filter)を組み合わせて使うことが多い。
Sortとの違い:Filterは「表示するコンテンツの件数を減らす」。Sortは「すべてのコンテンツを別の順序で並べ替える」。どちらも結果への影響が大きいため、ユーザーが現在の状態を把握できる可視化が必須。
3. 使い分け(When to use / When NOT to use)
3.1 When to use
- コンテンツが多く(20件以上)、ユーザーが特定の属性でカテゴリ分けして探したい場合
- ECサイト・求人サイト・記事一覧など多軸の条件で絞り込む必要がある場合
- ダッシュボード・管理画面でデータを条件で絞り込んで表示する場合
- Searchと組み合わせて「キーワード+条件」で精度高く絞り込む場合
3.2 When NOT to use
- コンテンツが10件以下で全件見渡せる → タブやセグメントコントロールで十分
- フィルター条件が1種類のみ → タブ(Tabs)またはセグメントコントロールを使う
- フォーム内の選択 →
Select/Radioを使う
3.3 代替UI(Alternatives)
- 条件が1種類(カテゴリのみ)→
Tabsまたはセグメントコントロール - 全文検索で十分 →
Search - 複雑なクエリ構築 →
Command Palette+ クエリビルダー
4. 設計判断の核(Decision Principles)
Filterの核は「どのフィルターがオンか」を常にユーザーに見せること——フィルターの状態が不明瞭だと、なぜ結果が少ないのかユーザーが理解できない。
判断の優先順位:① フィルター形式の選択 → ② アクティブ状態の可視化 → ③ 一括クリア → ④ 件数のリアルタイム表示
- 形式の選択基準:選択肢が2〜5個なら常時表示のチップ(Toggle Group)。6個以上や複数グループがある場合はドロップダウンまたはサイドバー。モバイルなら「フィルター」ボタン → モーダルシートのパターンが多い
- アクティブフィルターを削除可能なチップで表示:どの条件が選ばれているかを一覧できる場所に示す。各チップに × ボタンを付けて個別削除できるようにする
- 「すべてクリア」ボタンを常に用意:フィルターを全解除する手段を必ず提供する。「クリア」はフィルターが1つ以上アクティブな時のみ表示して、不要な操作対象を減らす
- 件数のリアルタイム表示:「N件が一致」をフィルター変更のたびに更新する。ユーザーは「あと何件になるか」を見ながらフィルターを調整する
5. 状態設計(States)
5.1 必須状態(Required)
- Default(フィルターなし):全件表示、フィルターは選択されていない
- Active(フィルターあり):選択済みフィルターをハイライト、一致件数を更新
- Empty(0件):すべての条件に一致するコンテンツがない場合
5.2 条件付き状態(Conditional)
- Loading:サーバーサイドフィルタリングで結果を取得中
- アクティブフィルター一覧表示:選択中の条件を削除可能なチップで表示
- 一括クリアボタン:フィルターが1つ以上アクティブな時のみ表示
5.3 State Gallery
| 状態 | 必須 | 何を伝えるか |
|---|---|---|
| Default | ✅ | フィルター未選択・全件表示 |
| Active | ✅ | 選択中のフィルターと一致件数 |
| Empty(0件) | ✅ | 条件に合うものがない |
| Loading | — | フィルター処理中 |
| アクティブ一覧 | — | 選択中の全条件を可視化 |
6. バリエーション設計(Variants)
フィルターの「形式」は選択肢の数と表示スペースで決める。
| バリアント | 選択肢数 | 配置 | 使用シーン |
|---|---|---|---|
| チップ(Toggle Group) | 2〜5個 | コンテンツ上部・常時表示 | タグ・ステータスなど少数の条件 |
| ドロップダウン | 6個以上 | ツールバー・折りたたみ | カテゴリ・属性など多数の条件 |
| サイドバー(Sidebar Filter) | 複数グループ | ページ左側 | ECサイト・求人サイト |
| モーダル(Filter Sheet) | 複数グループ | モバイル全画面 | モバイルの複雑なフィルター |
禁止パターン:フィルター変更後に「適用」ボタンを押さないと結果が更新されない → ユーザーは即座のフィードバックを期待する。可能な限りリアルタイムでフィルタリングする(「適用」ボタンはモバイルのモーダルフィルターでのみ許容)。
7. パターン集(Good / Bad / How to fix)
7.0 よく崩れる設計パターン(3つ)
- フィルター状態の不可視化:どのフィルターがオンになっているか分からず、なぜ結果が少ないのかユーザーが混乱する
- 「すべてクリア」なし:複数のフィルターをオンにしてしまった時、個別に全て外す必要があり手間がかかる
- フィルター変更後の結果件数が即時更新されない:「このフィルターを選ぶと何件になるか」が分からず、試行錯誤が増える
7.1 Bad(典型3つ)
- フィルターをチェックしても画面上でどの条件が選ばれているか分からず、誤って全件フィルタリングしてしまう
- 6個のフィルターを選択した状態で「クリア」ボタンがなく、1個ずつ手動で外す必要がある
- フィルター変更後に「適用」ボタンを押すまで結果が変わらず、ボタンが分かりにくい位置にある
7.2 Good(対になる3つ)
- アクティブなフィルターを「カテゴリ: UIコンポーネント ×」の形式で削除可能なチップで表示し、現在の絞り込み条件を常に可視化する
- フィルターが1つ以上オンの時にのみ「すべてクリア(N件をリセット)」ボタンを表示する
- クライアントサイドフィルタリングでリアルタイムに件数を更新し、「3件が一致」を常にコンテンツ上部に表示する
7.3 How to fix(手順)
- フィルターの状態を
MapまたはObjectで管理し、アクティブなフィルター一覧を別の場所(ヘッダー下など)に表示する - アクティブフィルターのチップに個別削除(×ボタン)と全体削除(すべてクリア)を追加する
- フィルター変更のたびに
filtered.lengthを更新して件数を表示する - 0件の時は Empty State を表示し「フィルターをリセットする」ボタンを提供する
8. ルール(Must / Better)
Must(守らないと壊れる)
Better(品質が跳ねる)
9. アクセシビリティ要件(必須)
Keyboard
- チップ型フィルター:
Tabでフォーカス移動、Space/Enterでオン/オフ切り替え - ドロップダウン型:
Tabでボタンにフォーカス、Enterで開く、↑・↓で選択肢移動、Escapeで閉じる
Focus
- アクティブなフィルターのクリアボタン(×)にもフォーカスを当てられること
- ドロップダウンを閉じた後、トリガーボタンにフォーカスを戻す
Screen Reader
- フィルターグループを
role="group"+aria-labelで意味論的にまとめる - チップ型トグルは
aria-pressedでオン/オフを伝える - チェックボックス型は
<fieldset>+<legend>でグループ化する - アクティブフィルターのクリアボタンに
aria-label={${value}のフィルターを削除}を付与する
<!-- チップ型フィルターグループ -->
<div role="group" aria-label="カテゴリでフィルター">
<button aria-pressed="true">UIコンポーネント</button>
<button aria-pressed="false">UIデザイン原則</button>
</div>
<!-- アクティブフィルターチップ -->
<span>
UIコンポーネント
<button aria-label="UIコンポーネントのフィルターを削除">×</button>
</span>
<!-- 結果件数のライブ通知 -->
<div aria-live="polite" aria-atomic="true">
3件が一致しています
</div>
Touch / Pointer
- フィルターチップ・チェックボックスのタップ領域は最低44×44px
- モバイルでは複数グループのフィルターを Sheet(ボトムシート)に収めてタップしやすくする
Contrast / Readability
- アクティブ状態のフィルターと非アクティブのコントラスト差を明確にする
- アクティブフィルターの背景色とテキストのコントラスト比は4.5:1以上
10. 実装メモ(Implementation Notes)
- フィルター状態のURLへの永続化は
useSearchParams(Next.js App Router)またはURLSearchParamsで実装する。ページリロード・共有URLでフィルターが復元される - インクリメンタルなクライアントサイドフィルタリングは
useMemoで計算してパフォーマンスを最適化する。依存配列にフィルター状態のすべてのキーを含める - サーバーサイドフィルタリングでは APIリクエストにデバウンス(300ms)を入れ、フィルター変更のたびに過剰なリクエストが飛ばないようにする
- shadcn/ui の
DropdownMenu+Checkboxの組み合わせでドロップダウン型フィルターが実装できる。アクティブ件数のバッジはBadgeコンポーネントをトリガーボタンに追加する
11. 関連リンク
- 関連するUIデザイン原則: スキャンしやすさ (Scannability), 情報密度 (Information Density), フィードバック (Feedback)
- 用語集(定義): 可読性 (Readability)
- 関連するUIコンポーネント(横): Search(検索), List(リスト), Table(テーブル), Badge(バッジ)
12. まとめ
Filterの設計は「どのフィルターがオンか」を常に見せることが最重要です。迷ったら 4. 設計判断の核 に戻り、アクティブフィルターの可視化・一括クリア・件数のリアルタイム表示の3点を確認してください。フィルター状態をURLに永続化することで、共有・ブックマーク・ブラウザバックへの対応も同時に解決します——「状態はURLに持つ」を設計の前提にすることで、後からの実装コストを大幅に下げられます。