タグ入力(TagsInput / PillsInput)ライブラリ比較

4つのアプローチでタグ入力UIを実装し、サジェスト・バリデーション・カスタムPillの違いを比較

共通の動作仕様

各デモではテキスト入力でタグを追加・削除できます。Enterキーで追加、×ボタンまたはBackspaceで削除します。

{
  "追加方法": "Enterキー / ×ボタン",
  "削除方法": "×ボタン / Backspaceキー",
  "バリデーション": "空文字・重複チェック",
  "サジェスト": "ライブラリによって異なる"
}

1. Mantine TagsInput

シンプルなタグ入力 — サジェスト候補・区切り文字・最大数制限が1コンポーネントで完結

読み込み中...

特徴

  • • Enterキーでタグ追加、×ボタンで削除
  • data プロパティにサジェスト候補を指定可能
  • splitChars でカンマ・スペース等の区切り文字を設定
  • maxTags で最大タグ数を制限

インストール

npm install @mantine/core @mantine/hooks
tsx
'use client';
import { MantineProvider, TagsInput } from '@mantine/core';
import '@mantine/core/styles.css';
import { useState } from 'react';

const suggestions = ['React', 'TypeScript', 'Next.js', 'Tailwind CSS', 'Node.js'];

export function MantineTagsInputDemo() {
  const [value, setValue] = useState<string[]>(['React', 'TypeScript']);

  return (
    <MantineProvider>
      <TagsInput
        label="タグを追加"
        placeholder="Enterキーで追加"
        value={value}
        onChange={setValue}
        data={suggestions}
        clearable
      />
    </MantineProvider>
  );
}

🤖 AIプロンプトテンプレート

React + Tailwind CSSで、Mantine TagsInputを使ったタグ入力UIを実装してください。
- 使用ライブラリ: @mantine/core の TagsInput コンポーネント
- MantineProvider でラップし、@mantine/core/styles.css をインポートすること
- value/onChange でタグの状態を管理すること
- data プロパティにサジェスト候補の配列を渡すこと
- splitChars={[",", " "]} でカンマ・スペース区切りに対応すること
- maxTags で最大タグ数を制限すること
- comboboxProps.transitionProps でドロップダウンのアニメーションを設定すること

⚠️ このプロンプトはあくまでたたき台です。AIの回答はモデルやバージョン、会話の文脈によって毎回異なります。意図通りに動かない場合は、条件を追記・修正してお使いください。

2. Mantine PillsInput + Combobox

カスタム実装用プリミティブ — Comboboxと組み合わせてドロップダウン付きマルチセレクトを構築

読み込み中...

特徴

  • PillsInput + Combobox の組み合わせで柔軟なUIを構築
  • • 選択済みアイテムをPill(チップ)形式で表示
  • • Backspaceキーで最後のPillをキーボードから削除
  • • ドロップダウンに選択済みチェックマークを表示

インストール

npm install @mantine/core @mantine/hooks
tsx
'use client';
import { MantineProvider, PillsInput, Pill, Combobox, useCombobox } from '@mantine/core';
import '@mantine/core/styles.css';
import { useState } from 'react';

const OPTIONS = ['React', 'TypeScript', 'Next.js', 'Tailwind CSS', 'Node.js'];

export function MantinePillsInputDemo() {
  const [selected, setSelected] = useState<string[]>([]);
  const [search, setSearch] = useState('');
  const combobox = useCombobox();

  const filtered = OPTIONS.filter(o => o.toLowerCase().includes(search.toLowerCase()));

  const toggle = (val: string) =>
    setSelected(prev => prev.includes(val) ? prev.filter(v => v !== val) : [...prev, val]);

  return (
    <MantineProvider>
      <Combobox store={combobox} onOptionSubmit={toggle}>
        <Combobox.DropdownTarget>
          <PillsInput onClick={() => combobox.openDropdown()}>
            <Pill.Group>
              {selected.map(s => (
                <Pill key={s} withRemoveButton onRemove={() => toggle(s)}>{s}</Pill>
              ))}
              <Combobox.EventsTarget>
                <PillsInput.Field
                  placeholder="検索..."
                  value={search}
                  onChange={e => { setSearch(e.currentTarget.value); combobox.openDropdown(); }}
                />
              </Combobox.EventsTarget>
            </Pill.Group>
          </PillsInput>
        </Combobox.DropdownTarget>
        <Combobox.Dropdown>
          <Combobox.Options>
            {filtered.map(o => (
              <Combobox.Option key={o} value={o}>
                {selected.includes(o) ? '✓ ' : ''}{o}
              </Combobox.Option>
            ))}
          </Combobox.Options>
        </Combobox.Dropdown>
      </Combobox>
    </MantineProvider>
  );
}

🤖 AIプロンプトテンプレート

React + Tailwind CSSで、Mantine PillsInput + Comboboxを使ったドロップダウン付きマルチセレクトUIを実装してください。
- 使用ライブラリ: @mantine/core の PillsInput、Pill、Combobox、useCombobox
- MantineProvider でラップし、@mantine/core/styles.css をインポートすること
- useCombobox フックで onDropdownClose/onDropdownOpen を制御すること
- Combobox.DropdownTarget 内に PillsInput を配置し、Pill.Group で選択済みPillを表示すること
- Combobox.EventsTarget 内に PillsInput.Field を配置し、検索文字列を管理すること
- Backspaceキーで最後のPillを削除する処理を onKeyDown に実装すること
- オプションに CheckIcon で選択済み状態を表示すること

⚠️ このプロンプトはあくまでたたき台です。AIの回答はモデルやバージョン、会話の文脈によって毎回異なります。意図通りに動かない場合は、条件を追記・修正してお使いください。

3. shadcn/ui(Input + Badge)

シンプルなタグ入力 — Input と Badge を組み合わせて基本的なタグ入力を実装

読み込み中...

特徴

  • • Enter またはカンマでタグ追加、×ボタン・Backspaceで削除
  • Input + Badge の組み合わせで実装
  • • サジェストが必要な場合は Combobox で別途実装
  • • バリデーション・サジェストは手動実装が必要

インストール

npx shadcn@latest add input badge
tsx
'use client';
import { useState, KeyboardEvent } from 'react';
import { Input } from '@/components/ui/input';
import { Badge } from '@/components/ui/badge';

export function ShadcnTagsInputDemo() {
  const [tags, setTags] = useState<string[]>(['React', 'TypeScript']);
  const [input, setInput] = useState('');

  const addTag = (value: string) => {
    const trimmed = value.trim();
    if (trimmed && !tags.includes(trimmed)) {
      setTags([...tags, trimmed]);
    }
    setInput('');
  };

  const removeTag = (tag: string) => setTags(tags.filter(t => t !== tag));

  const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
    if (e.key === 'Enter' || e.key === ',') {
      e.preventDefault();
      addTag(input);
    }
    if (e.key === 'Backspace' && input === '' && tags.length > 0) {
      removeTag(tags[tags.length - 1]);
    }
  };

  return (
    <div className="flex flex-wrap gap-2 p-2 border rounded-md min-h-10 items-center">
      {tags.map(tag => (
        <Badge key={tag} variant="secondary" className="gap-1">
          {tag}
          <button onClick={() => removeTag(tag)} className="ml-1 hover:text-destructive">×</button>
        </Badge>
      ))}
      <Input
        className="border-none shadow-none p-0 h-auto flex-1 min-w-20 focus-visible:ring-0"
        value={input}
        onChange={e => setInput(e.target.value)}
        onKeyDown={handleKeyDown}
        onBlur={() => input && addTag(input)}
        placeholder="タグを入力..."
      />
    </div>
  );
}

🤖 AIプロンプトテンプレート

React + Tailwind CSSで、shadcn/uiを使ったタグ入力UIを実装してください。
- shadcn/ui には TagsInput 専用コンポーネントがないため、Input と Badge を組み合わせて実装する
- npx shadcn@latest add input badge でインストール
- div コンテナ内に Badge と Input を並べてタグ入力を実現
- Input の className で border / shadow / focus-ring を非表示にして自然に統合する
- Enterキーでタグ追加、Backspaceキーで最後のタグを削除
- BadgeのchildrenにタグテキストとXボタンを配置
- onBlur で未確定のテキストを自動追加

⚠️ このプロンプトはあくまでたたき台です。AIの回答はモデルやバージョン、会話の文脈によって毎回異なります。意図通りに動かない場合は、条件を追記・修正してお使いください。

4. Chakra UI TagsInput

シンプルで直感的なタグ入力 — TagsInput.Root の複合コンポーネントパターンで構成

読み込み中...

特徴

  • TagsInput.Root の複合パターンで各パーツを明示的に制御
  • TagsInput.Item / ItemText / ItemDeleteTrigger でタグの見た目をカスタマイズ
  • onValueChange で値の変更を管理
  • ClearTrigger で全タグ一括削除に対応
  • • WAI-ARIA 準拠でアクセシビリティが標準対応

インストール

npm install @chakra-ui/react @emotion/react
tsx
'use client'
import { useState } from 'react'
import { createSystem, defaultConfig, ChakraProvider, TagsInput } from '@chakra-ui/react'

const system = createSystem(defaultConfig, { globalCss: {}, preflight: false })

export function ChakraTagsInputDemo() {
  const [value, setValue] = useState<string[]>(['React', 'TypeScript'])
  return (
    <ChakraProvider value={system}>
      <TagsInput.Root
        value={value}
        onValueChange={(e) => setValue(e.value)}
        maxW="sm"
      >
        <TagsInput.Label>タグを追加</TagsInput.Label>
        <TagsInput.Control>
          {value.map((tag, i) => (
            <TagsInput.Item key={i} index={i} value={tag}>
              <TagsInput.ItemText>{tag}</TagsInput.ItemText>
              <TagsInput.ItemDeleteTrigger />
            </TagsInput.Item>
          ))}
          <TagsInput.Input placeholder="Enterキーで追加" />
        </TagsInput.Control>
        <TagsInput.ClearTrigger>すべて削除</TagsInput.ClearTrigger>
      </TagsInput.Root>
    </ChakraProvider>
  )
}

🤖 AIプロンプトテンプレート

React + Tailwind CSSで、Chakra UI v3のTagsInputを使ったタグ入力UIを実装してください。
- 使用ライブラリ: @chakra-ui/react の TagsInput コンポーネント(v3)
- ChakraProvider に createSystem(defaultConfig, { globalCss: {}, preflight: false }) を渡すこと
- TagsInput.Root / Control / Item / ItemText / ItemDeleteTrigger / Input の複合パターンで実装
- onValueChange で値の変更を管理
- TagsInput.ClearTrigger で全タグ一括削除に対応
- WAI-ARIA 準拠でキーボード操作(Enter で追加、Backspace で削除)が標準対応

⚠️ このプロンプトはあくまでたたき台です。AIの回答はモデルやバージョン、会話の文脈によって毎回異なります。意図通りに動かない場合は、条件を追記・修正してお使いください。

5. カスタム実装(CSS / Tailwind)

ライブラリ不要の完全カスタム実装 — 重複チェック・空文字バリデーション・Backspace削除

ReactTypeScript

Enterで追加 / Backspaceで最後のタグを削除 / 重複チェック・空文字バリデーション内蔵

タグ数: 2

特徴

  • • 外部ライブラリ不要で依存関係が最小限
  • • 空文字バリデーション・重複チェックを自前で実装
  • • Backspaceキーで最後のタグを削除
  • • コンテナクリックでinputにフォーカス

インストール

追加パッケージ不要(React + Tailwind CSS のみ)
tsx
'use client';
import { useState, useRef } from 'react';

export function CustomTagsInputDemo() {
  const [tags, setTags] = useState<string[]>(['React', 'TypeScript']);
  const [input, setInput] = useState('');
  const [error, setError] = useState('');
  const inputRef = useRef<HTMLInputElement>(null);

  const addTag = (value: string) => {
    const trimmed = value.trim();
    if (!trimmed) { setError('空のタグは追加できません'); return; }
    if (tags.includes(trimmed)) { setError(`「${trimmed}」はすでに追加されています`); return; }
    setTags(prev => [...prev, trimmed]);
    setInput('');
    setError('');
  };

  const removeTag = (tag: string) => setTags(prev => prev.filter(t => t !== tag));

  return (
    <div className="space-y-2">
      <div
        className="flex flex-wrap gap-2 p-2 border-2 rounded-lg min-h-[48px] cursor-text border-gray-300 focus-within:border-blue-500"
        onClick={() => inputRef.current?.focus()}
      >
        {tags.map(tag => (
          <span key={tag} className="inline-flex items-center gap-1 px-2.5 py-0.5 bg-blue-100 text-blue-800 rounded-full text-sm">
            {tag}
            <button onClick={() => removeTag(tag)} className="w-4 h-4 rounded-full flex items-center justify-center hover:bg-blue-200">×</button>
          </span>
        ))}
        <input
          ref={inputRef}
          value={input}
          onChange={e => { setInput(e.target.value); setError(''); }}
          onKeyDown={e => {
            if (e.key === 'Enter') { e.preventDefault(); addTag(input); }
            if (e.key === 'Backspace' && input === '' && tags.length > 0) removeTag(tags[tags.length - 1]);
          }}
          placeholder={tags.length === 0 ? 'タグを入力してEnter' : ''}
          className="flex-1 min-w-[120px] outline-none bg-transparent text-sm"
        />
      </div>
      {error && <p className="text-xs text-red-500">{error}</p>}
    </div>
  );
}

🤖 AIプロンプトテンプレート

React + Tailwind CSSで、ライブラリ不要のカスタムタグ入力UIを実装してください。
- 使用ライブラリ: React + Tailwind CSS のみ(外部タグ入力ライブラリなし)
- useState でタグの配列と入力文字列を管理すること
- Enterキーでタグを追加、Backspaceキー(入力が空の状態)で最後のタグを削除する処理を実装すること
- 空文字バリデーション・重複チェックを行い、エラーメッセージを表示すること
- タグはrounded-fullのバッジ形式で表示し、×ボタンで個別削除できるようにすること
- コンテナをクリックするとinputにフォーカスするよう実装すること

⚠️ このプロンプトはあくまでたたき台です。AIの回答はモデルやバージョン、会話の文脈によって毎回異なります。意図通りに動かない場合は、条件を追記・修正してお使いください。

ライブラリ比較表

項目Mantine TagsInputMantine PillsInputshadcn/uiChakra UIカスタム実装
サジェスト候補内蔵(data prop)Comboboxで自作⚠️ Comboboxで別途実装⚠️ Combobox との組み合わせで実装自作
カスタムPill限定的完全カスタム可能✅ Badge でカスタマイズ✅ ItemText / DeleteTrigger でカスタム完全カスタム可能
バリデーション内蔵(重複・空文字)自作⚠️ 手動で実装⚠️ onInvalid で自作自作
区切り文字設定splitChars prop自作✅ onKeyDown で実装⚠️ 非対応(Enter のみ)自作
最大タグ数maxTags prop自作⚠️ 手動で実装✅ max prop自作
キーボード操作Enter追加・Backspace削除Backspace削除(自作)✅ Enter追加・Backspace削除✅ Enter追加・Backspace削除Enter追加・Backspace削除
バンドルサイズ中程度(Mantine)中程度(Mantine)✅ 使った分だけ⚠️ 中(emotion含む)最小

選択のポイント

Mantine TagsInput — シンプルなタグ入力に最適。サジェスト候補・区切り文字・最大数制限が1行のpropsで設定でき、学習コストが低い。Mantineをすでに使っているプロジェクトでの採用を推奨。

Mantine PillsInput + Combobox — ドロップダウン付きのマルチセレクトを構築したい場合に。TagsInputより実装量は増えるが、Pillの見た目やComboboxの動作を細かくカスタマイズできる上級パターン。

shadcn/ui(Input + Badge) — TagsInput専用コンポーネントはないが、InputとBadgeを組み合わせることで基本的なタグ入力を実装できる。サジェストやバリデーションが必要な場合はMantineのTagsInputや他のライブラリが適している。

Chakra UI TagsInput — TagsInput.Root の複合コンポーネントパターンでタグの各パーツを明示的に制御できる。WAI-ARIA 準拠でアクセシビリティが標準対応。Chakra UI をすでに採用しているプロジェクトでの採用を推奨。

カスタム実装 — 外部ライブラリへの依存を避けたい場合や、独自のデザインシステムに完全に合わせたい場合に最適。バリデーションロジックを自前で制御できる。

タグ入力UI(TagsInput)は、テキスト入力でタグを追加・削除するフォームコンポーネント。入力した文字列がタグ(Pill)として表示される。

ブログ記事のタグ付け・スキルセット入力・メールアドレスの複数入力・フィルター条件の設定など、複数の値をインタラクティブに管理する場面で使われる。

主なバリエーション
  • 基本型(Enter で追加)
  • サジェスト型(候補表示)
  • バリデーション付き型
  • カスタムPill型
  • 上限数制限型