Kanban Board
DnD & InteractionKanban board with draggable columns and cards.
Preview
To Do2
Research competitors
Sketch wireframes
In Progress1
Build components
Done1
Set up repo
Usage
example.jsx
import { KanbanBoard } from "@/components/ui/kanban-board";
export default function Example() {
return <KanbanBoard />;
}Source Code
Copy this file into components/ui/kanban-board.jsx in your project.
kanban-board.jsx
"use client";
import { forwardRef, useState } from "react";
import { cn } from "@/lib/utils";
const KanbanBoard = forwardRef(({ className, columns: initial = [], ...props }, ref) => {
const [columns, setColumns] = useState(initial);
const [dragging, setDragging] = useState(null); // { colIdx, itemIdx }
const onDragStart = (colIdx, itemIdx) => setDragging({ colIdx, itemIdx });
const onDropOnColumn = (targetColIdx) => {
if (!dragging || dragging.colIdx === targetColIdx) { setDragging(null); return; }
const newCols = columns.map((col) => ({ ...col, items: [...col.items] }));
const [item] = newCols[dragging.colIdx].items.splice(dragging.itemIdx, 1);
newCols[targetColIdx].items.push(item);
setColumns(newCols);
setDragging(null);
};
return (
<div ref={ref} className={cn("flex gap-4 overflow-x-auto p-4", className)} {...props}>
{columns.map((col, ci) => (
<div key={ci}
onDragOver={(e) => e.preventDefault()}
onDrop={() => onDropOnColumn(ci)}
className="flex w-72 shrink-0 flex-col rounded-lg border bg-muted/30"
>
<div className="flex items-center justify-between border-b px-3 py-2">
<span className="text-sm font-semibold">{col.title}</span>
<span className="rounded-full bg-muted px-2 py-0.5 text-xs text-muted-foreground">{col.items.length}</span>
</div>
<div className="flex-1 space-y-2 p-3">
{col.items.map((item, ii) => (
<div key={ii} draggable
onDragStart={() => onDragStart(ci, ii)}
className="cursor-grab rounded-md border bg-background p-3 text-sm shadow-sm active:cursor-grabbing"
>
{typeof item === "string" ? item : item.title || JSON.stringify(item)}
</div>
))}
</div>
</div>
))}
</div>
);
});
KanbanBoard.displayName = "KanbanBoard";
export { KanbanBoard };
Quick Install
Make sure you have the cn() utility set up. It requires clsx and tailwind-merge.
npm install clsx tailwind-merge