Victor

Kanban Board

Component

About

Columns and cards with smooth FLIP drag-and-drop, keyboard movement and empty states. The drop slot grows to the card's height.

Built with React and Tailwind. Copy the prompt and paste it into Claude Code, Cursor or Lovable: your agent adds Kanban Board to your project and fits it to your theme.

Code

How Kanban Board is used: the demo's code. The component's own source comes with a free account.

Usage.tsx

"use client";

import { useState } from "react";
import {
  KanbanBoard,
  KanbanBoardCard,
  KanbanBoardCardMeta,
  KanbanBoardCards,
  KanbanBoardCardTitle,
  KanbanBoardColumn,
  KanbanBoardColumnCount,
  KanbanBoardColumnEmpty,
  KanbanBoardColumnHeader,
  KanbanBoardColumnTitle,
  type KanbanBoardValue,
} from "@/components/ui/kanban-board";

const columns = { backlog: "Backlog", progress: "In progress", review: "In review" };
const tags = {
  Security: "bg-destructive/14 text-destructive",
  Support: "bg-primary/14 text-primary",
  Billing: "bg-secondary text-secondary-foreground",
  Data: "bg-muted text-muted-foreground",
};
const cards: Record<string, { title: string; tag: keyof typeof tags; due: string }> = {
  "OPS-112": { title: "Rotate staging API keys", tag: "Security", due: "Due Oct 3" },
  "OPS-118": { title: "Document the on-call handoff", tag: "Support", due: "No due date" },
  "OPS-121": { title: "Migrate invoices to the new ledger", tag: "Billing", due: "Due Oct 9" },
  "OPS-124": { title: "Fix CSV export for archived rows", tag: "Data", due: "Due Oct 1" },
};

export default function KanbanBoardDemo() {
  const [value, setValue] = useState<KanbanBoardValue>({
    backlog: ["OPS-112", "OPS-118"],
    progress: ["OPS-121", "OPS-124"],
    review: [],
  });

  return (
    <div className="flex min-h-[420px] w-full items-center justify-center p-6">
      <div className="w-full max-w-[860px]">
        <KanbanBoard variant="board" value={value} onValueChange={setValue}>
          {Object.entries(value).map(([column, ids]) => {
            const label = columns[column as keyof typeof columns];
            return (
              <KanbanBoardColumn key={column} value={column} label={label}>
                <KanbanBoardColumnHeader>
                  <KanbanBoardColumnTitle>{label}</KanbanBoardColumnTitle>
                  <KanbanBoardColumnCount />
                </KanbanBoardColumnHeader>
                <KanbanBoardCards>
                  {ids.map((id) => {
                    const card = cards[id];
                    if (!card) return null;
                    return (
                      <KanbanBoardCard key={id} value={id} label={card.title}>
                        <KanbanBoardCardTitle>{card.title}</KanbanBoardCardTitle>
                        <KanbanBoardCardMeta>
                          <span className={`rounded-[6px] px-1.5 text-[11px]/[18px] font-medium ${tags[card.tag]}`}>
                            {card.tag}
                          </span>
                          <span>{id}</span>
                          <span>{card.due}</span>
                        </KanbanBoardCardMeta>
                      </KanbanBoardCard>
                    );
                  })}
                </KanbanBoardCards>
                <KanbanBoardColumnEmpty>Nothing waiting for review.</KanbanBoardColumnEmpty>
              </KanbanBoardColumn>
            );
          })}
        </KanbanBoard>
      </div>
    </div>
  );
}

Dependencies

More by Victor

View all
  • Thinking

    Thinking

    14
  • Command Menu

    Command Menu

    3
  • Prompt Composer

    Prompt Composer

  • Installment Picker

    Installment Picker

  • Pix Payment

    Pix Payment

  • Approval Card

    Approval Card

Explore more

Part of Uai — browse the full library on 21st.dev.