# Kanban board

> A three-column kanban board with card counts, tagged task cards, and an add-card affordance per column.

A shadcn/ui block from the [@quill](https://www.quilldesignsystem.com) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/quill/kanban).

- Page, with a live preview: https://shadcn.exchange/r/quill/kanban
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/quill/kanban-light-25f5f6123c1a5f11.png
- Registry item: https://www.quilldesignsystem.com/r/kanban.json
- Categories: productivity
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://www.quilldesignsystem.com/r/kanban.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- Registry items: `badge`, `https://www.quilldesignsystem.com/r/icon.json`

## Notes from the registry

A three-column kanban board with card counts, tagged task cards, and an add-card affordance per column. Do: Keep each column's card-count badge in sync with its actual card list — it's a running total, not a caption. Don't: Let the count badge drift out of sync when cards move between columns. Use data-table when status is one column among many sortable fields — a table reads better than columns once you need to sort or filter across many records. Use onboarding when the steps are a single sequential checklist, not parallel work items across independent columns.

## Files

### `registry/blocks/kanban.tsx`

```tsx
import { Badge } from '@/components/ui/badge'
import { Icon } from '@/components/ui/icon'

const columns = [
  { title: 'Backlog', cards: [{ t: 'Audit color contrast', tag: 'a11y' }, { t: 'Draft migration guide', tag: 'docs' }] },
  { title: 'In progress', cards: [{ t: 'Wave B components', tag: 'design' }, { t: 'Icon gallery search', tag: 'feat' }] },
  { title: 'Done', cards: [{ t: 'Token foundation', tag: 'core' }, { t: 'Material Symbols', tag: 'icons' }] },
]

/** A three-column kanban board with card counts, tagged task cards, and an add-card affordance per column. */
export function Kanban() {
  return (
    <div className="flex gap-4 text-foreground">
      {columns.map((col) => (
        <div key={col.title} className="flex w-64 shrink-0 flex-col gap-3 rounded-xl bg-muted/50 p-3">
          <div className="flex items-center justify-between px-1">
            <span className="text-sm font-medium">{col.title}</span>
            <Badge variant="secondary">{col.cards.length}</Badge>
          </div>
          <div className="flex flex-col gap-2">
            {col.cards.map((c) => (
              <div key={c.t} className="flex flex-col gap-2 rounded-lg bg-card p-3 ring-1 ring-foreground/10">
                <span className="text-sm">{c.t}</span>
                <div className="flex items-center justify-between">
                  <Badge variant="outline">{c.tag}</Badge>
                  <div className="flex size-6 items-center justify-center rounded-full bg-muted text-2xs font-medium text-muted-foreground">
                    AL
                  </div>
                </div>
              </div>
            ))}
          </div>
          <button className="flex items-center gap-1.5 rounded-lg px-1 py-1.5 text-sm text-muted-foreground transition-colors hover:text-foreground outline-hidden focus-visible:ring-3 focus-visible:ring-ring/50">
            <Icon name="add" size={16} /> Add card
          </button>
        </div>
      ))}
    </div>
  )
}
```
