SCALE — Build Lab
開発パターン · REACT PATTERN

レスポンシブテーブル

CATEGORY開発パターン TYPEReact Pattern EFFORT90〜240分 DIFFICULTY
PRIMARY CODE
tsx
// テーブル → モバイルでカード表示に自動切替
type Col<T> = { key: keyof T; label: string; render?: (row: T) => React.ReactNode };

export function ResponsiveTable<T>({ rows, cols }: { rows: T[]; cols: Col<T>[] }) {
  return (
    <>
      {/* デスクトップ:通常テーブル */}
      <table className="hidden md:table w-full text-sm">
        <thead className="text-left text-zinc-400">
          <tr>{cols.map((c) => <th key={String(c.key)} className="p-3">{c.label}</th>)}</tr>
        </thead>
        <tbody>
          {rows.map((row, i) => (
            <tr key={i} className="border-t border-zinc-800">
              {cols.map((c) => (
                <td key={String(c.key)} className="p-3">
                  {c.render ? c.render(row) : String((row as any)[c.key] ?? '')}
                </td>
              ))}
            </tr>
          ))}
        </tbody>
      </table>

      {/* モバイル:カード表示 */}
      <div className="md:hidden space-y-3">
        {rows.map((row, i) => (
          <div key={i} className="bg-zinc-900 rounded-xl p-4 space-y-2">
            {cols.map((c) => (
              <div key={String(c.key)} className="flex justify-between gap-2">
                <span className="text-xs text-zinc-500">{c.label}</span>
                <span className="text-sm text-zinc-100 text-right">
                  {c.render ? c.render(row) : String((row as any)[c.key] ?? '')}
                </span>
              </div>
            ))}
          </div>
        ))}
      </div>
    </>
  );
}
前提条件
Tailwind CSS v4TypeScript 5
USE CASES
  • 全テーブル

レスポンシブテーブル

:LiTarget: 用途

モバイルでカード表示に切り替わるテーブルパターン。

:LiSparkle: 特徴

  • ブレークポイント切替
  • カード表示
  • スクロール対応

:LiCode: コード(コピペ用)

// テーブル → モバイルでカード表示に自動切替
type Col<T> = { key: keyof T; label: string; render?: (row: T) => React.ReactNode };

export function ResponsiveTable<T>({ rows, cols }: { rows: T[]; cols: Col<T>[] }) {
  return (
    <>
      {/* デスクトップ:通常テーブル */}
      <table className="hidden md:table w-full text-sm">
        <thead className="text-left text-zinc-400">
          <tr>{cols.map((c) => <th key={String(c.key)} className="p-3">{c.label}</th>)}</tr>
        </thead>
        <tbody>
          {rows.map((row, i) => (
            <tr key={i} className="border-t border-zinc-800">
              {cols.map((c) => (
                <td key={String(c.key)} className="p-3">
                  {c.render ? c.render(row) : String((row as any)[c.key] ?? '')}
                </td>
              ))}
            </tr>
          ))}
        </tbody>
      </table>

      {/* モバイル:カード表示 */}
      <div className="md:hidden space-y-3">
        {rows.map((row, i) => (
          <div key={i} className="bg-zinc-900 rounded-xl p-4 space-y-2">
            {cols.map((c) => (
              <div key={String(c.key)} className="flex justify-between gap-2">
                <span className="text-xs text-zinc-500">{c.label}</span>
                <span className="text-sm text-zinc-100 text-right">
                  {c.render ? c.render(row) : String((row as any)[c.key] ?? '')}
                </span>
              </div>
            ))}
          </div>
        ))}
      </div>
    </>
  );
}

:LiHandPointer: 使い方

対象プロジェクトに該当ファイルをコピーして、props を流し込むだけ。

:LiAlertCircle: 注意事項

  • 依存パッケージを忘れず追加