# React Kanban Board: Drag, WIP-Limits, Smartphone-Layout

> Bau ein React Kanban Board mit Drag per Maus, Touch und Tastatur, WIP-Limits und Inline-Anlegen von Karten, das am Smartphone immer nur eine Spalte zeigt.

- Autor: [Serban Rusu](https://wingo-ui.com/blog/authors/serban), Founder of Wingo UI
- Veröffentlicht: 16. Okt. 2026
- Kategorie: [Komponenten-Guides](https://wingo-ui.com/de/blog/category/components)
- Lesezeit: 9 Min.
- Kanonische URL: https://wingo-ui.com/de/blog/react-kanban-board
- Englische Fassung: https://wingo-ui.com/blog/react-kanban-board

## Kurz gesagt

Ein React Kanban Board für die Produktion braucht Drag per Maus, langes Drücken auf Touch-Geräten, Leertaste und Pfeiltasten sowie ein Menü „Move to“ für alle, die nicht ziehen können. Dazu kommen WIP-Limits, die warnen oder das Ablegen verweigern, und ein Smartphone-Layout, das immer nur eine Spalte zeigt. Das Kanban von Wingo UI erledigt das aus einem columns-Array und einem kontrollierten value; die Deal Pipeline ergänzt Wahrscheinlichkeiten pro Phase und gewichtete Summen.

Du brauchst ein React Kanban Board für Aufgaben, Bestellungen oder eine Sales-Pipeline. Karten mit der Maus zwischen Spalten zu ziehen ist der leichte Teil, und dort hören die meisten Tutorials auf. Dann öffnet jemand das Board auf dem Smartphone: Die fünf Spalten schrumpfen zu schmalen Streifen, ein Wischen zieht eine Karte, obwohl man scrollen wollte, wer nur die Tastatur nutzt, kann nichts verschieben, und „In progress“ füllt sich unbemerkt mit zwölf Karten. Dieser Guide zeigt, was ein Board für die Produktion braucht, welche Props im [Kanban](https://wingo-ui.com/components/kanban) von Wingo UI zählen, und baut ein komplettes Beispiel mit Speichern, Anlegen von Karten direkt in der Spalte und WIP-Limits.

## Was muss eine React Kanban Board Komponente können?

Sechs Dinge: drei Arten zu ziehen, ein Weg zum Verschieben ohne Ziehen, WIP-Limits, Anlegen direkt in der Spalte, ein Smartphone-Layout und Ansagen für Screenreader. Die meisten Demos einer React Kanban Board Komponente zeigen nur das Erste, eine Maus auf einem breiten Bildschirm. An dieser Liste messen wir unser eigenes Board:

1. **Drei Arten zu ziehen.** Mit der Maus nach 8px Bewegung (damit ein Klick die Karte weiterhin öffnet), per Touch nach 250ms langem Drücken (damit ein Wischen weiterhin scrollt), per Tastatur mit Leertaste und Pfeiltasten.
2. **Ein Weg zum Verschieben ohne Ziehen.** Das WCAG-2.2-Erfolgskriterium [2.5.7 Dragging Movements](https://www.w3.org/WAI/WCAG22/Understanding/dragging-movements.html) (Stufe AA) verlangt für jede Ziehbewegung eine Alternative mit einem einzelnen Zeiger, es sei denn, das Ziehen ist unverzichtbar. Ein Menü „Move to“ deckt das ab.
3. **WIP-Limits**, die warnen oder das Ablegen verweigern.
4. **Anlegen direkt in der Spalte**, das für die nächste Karte offen bleibt.
5. **Ein Smartphone-Layout**, das immer nur eine Spalte zeigt.
6. **Ansagen** jeder Verschiebung für Screenreader.

Punkt 5 lassen Demos gern weg: Fünf Spalten mit 288px brauchen 1.440px plus Abstände. Auf einem 390px breiten Bildschirm scrollst du also entweder seitwärts an Spaltenstreifen vorbei, oder die Karten werden so schmal, dass Titel nach zwei Wörtern umbrechen.

## Welche React Kanban Library passt zu meinem Projekt?

Dice UI ist eine kostenlose Quellcode-Komponente, die du über die shadcn CLI installierst, SVAR ist ein fertiges Widget für große Boards, und das Kanban von Wingo UI ist eine Pro-Komponente als Quellcode mit eingebautem Smartphone-Layout und WIP-Limits. Diese drei Namen tauchen auf, wenn du nach einer React Kanban Library oder einem shadcn Kanban suchst. Die Fakten stammen aus der öffentlichen Doku der Projekte (die MIT-Lizenz von Dice UI aus dem GitHub-Repository), Stand Oktober 2026.

| Option | Was es ist | Drag-Engine | Tastatur | Smartphone-Layout und WIP-Limits |
|---|---|---|---|---|
| [Dice UI Kanban](https://diceui.com/docs/components/base/kanban) | Kostenlose Quellcode-Komponente unter MIT, Installation mit `npx shadcn@latest add @diceui/kanban` | dnd kit | Enter oder Leertaste hebt an, Pfeiltasten bewegen, Escape bricht ab | In der Doku nicht erwähnt |
| [SVAR React Kanban](https://svar.dev/react/kanban/) | Fertige Library, Core-Edition unter MIT plus kostenpflichtige PRO-Edition (Export als PDF, PNG und Excel, Rückgängig und Wiederholen, dynamisches Nachladen von Spalten) | Eingebaut, keine zusätzliche Drag-Library | Auf der Produktseite nicht erwähnt | Auf der Produktseite nicht erwähnt |
| [Wingo UI Kanban](https://wingo-ui.com/components/kanban) | Pro-Komponente als Quellcode, Installation mit `npx wingo-ui@latest add kanban` | dnd kit | Leertaste hebt an, Pfeiltasten bewegen auch über Spalten hinweg, Leertaste oder Enter legt ab, Escape bricht ab | Eine Spalte pro Ansicht unter 576px, `limit` pro Spalte mit Warnen oder Blockieren |

Dice UI passt, wenn du kostenlose, kombinierbare Bausteine willst und das Verhalten am Smartphone selbst bauen möchtest. SVAR passt, wenn du ein konfiguriertes Widget willst: Die Produktseite nennt Virtualisierung von Karten und Spalten, und das zählt, sobald ein Board Tausende Karten hält. Der Rest dieses Guides behandelt das Board von Wingo UI.

## Welche Props braucht ein React Kanban Board?

Zwei Arrays. `columns` listet die Spalten in ihrer Reihenfolge, und `value` (oder `defaultValue`) listet die Karten, jede mit einer `columnId`. Nach einem Ablegen, einem „Move to“ oder einer neuen Karte ruft das Board `onValueChange` mit dem kompletten neuen Array auf.

```bash
npx wingo-ui@latest add kanban
```

```tsx
"use client";

import { useState } from "react";
import { Kanban, type KanbanColumnData, type KanbanItem } from "@/components/ui/kanban";

const columns: KanbanColumnData[] = [
  { id: "lead", title: "Neuer Lead", tone: "info" },
  { id: "offer", title: "Angebot gesendet", limit: 5 },
  { id: "won", title: "Gewonnen", tone: "success" },
];

export function Pipeline() {
  const [deals, setDeals] = useState<KanbanItem[]>([
    { id: "c02", columnId: "offer", title: "Northwind Logistics", amount: 48920, dueDate: "2026-09-26" },
    { id: "c05", columnId: "won", title: "Summit Builders", amount: 103400, assignees: [{ name: "Daniel Reed" }] },
  ]);

  return (
    <div className="h-[560px]">
      <Kanban columns={columns} value={deals} onValueChange={(next) => setDeals(next)} locale="en-US" />
    </div>
  );
}
```

Das Board füllt die Höhe seines Containers, also gib dem Wrapper eine.

Eine Karte kennt die Felder `title`, `description`, `tags`, `assignees`, `dueDate` (rot, sobald die Karte überfällig ist), `priority`, `amount`, ein `cover`-Bild im Format 16:9 und eine gedämpfte `meta`-Zeile. Beträge werden im Spaltenkopf in `currency` summiert (standardmäßig USD); setz `total` einer Spalte, um die Summe zu überschreiben, oder `null`, um sie auszublenden. Danach änderst du am häufigsten `limitBehavior`, `columnWidth` (standardmäßig 288px), `size` (`sm` für dichte Übersichts-Boards) und `readOnly`.

Zieh eine Karte in eine andere Spalte. Dann spring per Tab zu einer Karte und drück die Leertaste, um sie mit den Pfeiltasten zu bewegen. Im ⋯-Menü einer Karte findest du „Move to“.

> Live-Demo (Kanban): Zieh einen Deal nach Negotiation und sieh zu, wie sich die Spaltensummen ändern. Spring per Tab zu einer Karte, drück die Leertaste, beweg sie mit den Pfeiltasten und drück zum Ablegen erneut die Leertaste, oder nutz ⋯ und dann Move to. Probier sie unter [Kanban](https://wingo-ui.com/components/kanban) aus und installier sie mit `npx wingo-ui@latest add kanban`.

## Wie funktionieren Kanban WIP-Limits im Board?

Ein WIP-Limit (Work in Progress) ist die Höchstzahl an Karten, die eine Spalte gleichzeitig halten soll. Kanban WIP-Limits sollen einen Engpass sichtbar machen: Wenn „In review“ voll ist, schließt das Team erst Reviews ab, bevor es Neues anfängt. Die [Column Constraints](https://confluence.atlassian.com/jirasoftwareserver/configuring-columns-938845277.html) von Jira (dokumentiert für Jira Software Server) färben eine Spalte rot, wenn sie ihr Maximum überschreitet, und gelb, wenn sie unter ihrem Minimum liegt, rein als visuelles Signal.

Das Kanban unterstützt beide Varianten. Gib einer Spalte ein `limit`, und ihr Zähler zeigt „3 / 5“:

- `limitBehavior="warn"` (der Standard): Am Limit wird der Zähler bernsteinfarben, darüber rot, mit einem einmaligen Pulsieren und einer Textzeile („Over the limit: 6 of 5“). Das Ablegen geht trotzdem durch.
- `limitBehavior="block"`: Eine volle Spalte verweigert das Ablegen, wird beim Ziehen abgedunkelt, und ihr Eintrag im Menü „Move to“ ist deaktiviert.

```tsx
{ id: "doing", title: "In Arbeit", tone: "info", limit: 3, description: "Höchstens 3 gleichzeitig" }
```

Nimm warn für Teams, die das Limit als Anlass für ein Gespräch sehen, und block für Boards, bei denen das Limit eine echte Kapazität ist, etwa eine Packstation, die nur eine feste Zahl an Bestellungen schafft. Eine Spalte mit `disabled: true` nimmt gar keine Karten an, ihre eigenen Karten können sie aber weiterhin verlassen.

> Live-Demo (Kanban): Proposal sent hat ein Limit von 5 und hält 4 Deals. Zieh einen weiteren hinein und versuch dann einen zweiten: Die volle Spalte wird abgedunkelt und verweigert ihn. Probier sie unter [Kanban](https://wingo-ui.com/components/kanban) aus und installier sie mit `npx wingo-ui@latest add kanban`.

## Wie speichere ich Verschiebungen und lege Karten über den Server an?

Aktualisier den State sofort, schick die Änderung los und setz sie zurück, wenn der Request scheitert. `onValueChange` bekommt das neue Array und bei Verschiebungen ein zweites Argument: `{ itemId, fromColumnId, toColumnId, fromIndex, toIndex }`. `toIndex` ist die Position in der Zielspalte, nicht im flachen `value`-Array. Schick ihn also mit der Spalten-ID mit und lass den Server die Sortierschlüssel dieser Spalte neu nummerieren. `onMove` läuft, bevor eine Verschiebung übernommen wird; gib `false` zurück, um sie abzubrechen, und die Karte fliegt zurück.

`onAddCard` blendet unter jeder Spalte „+ Add card“ ein. Gib die neue Karte oder ein Promise darauf zurück, und das Board fügt sie am Ende der Spalte ein. Solange das Promise läuft, zeigt der Add-Button einen Ladezustand, und wenn es fehlschlägt, bleibt der Text im Eingabefeld, damit nichts verloren geht. Enter legt die Karte an und lässt das Feld für die nächste offen, Shift + Enter erzeugt einen Zeilenumbruch, und Escape schließt es.

```tsx
"use client";

import { useState } from "react";
import { Kanban, type KanbanColumnData, type KanbanItem } from "@/components/ui/kanban";

export function TaskBoard({ columns, initial }: { columns: KanbanColumnData[]; initial: KanbanItem[] }) {
  const [tasks, setTasks] = useState(initial);

  return (
    <div className="h-[calc(100dvh-8rem)]">
      <Kanban
        columns={columns}
        value={tasks}
        limitBehavior="block"
        // erledigte Karten dürfen nicht zurück ins Backlog
        onMove={(event) => !(event.fromColumnId === "done" && event.toColumnId === "todo")}
        onValueChange={(next, move) => {
          const previous = tasks;
          setTasks(next);
          if (!move) return;
          fetch(`/api/tasks/${move.itemId}`, {
            method: "PATCH",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({ columnId: move.toColumnId, index: move.toIndex }),
          })
            .then((response) => {
              if (!response.ok) setTasks(previous);
            })
            .catch(() => setTasks(previous));
        }}
        onAddCard={async (columnId, title) => {
          const response = await fetch("/api/tasks", {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({ columnId, title }),
          });
          if (!response.ok) throw new Error("Nicht gespeichert");
          return (await response.json()) as KanbanItem;
        }}
        locale="en-US"
      />
    </div>
  );
}
```

Wenn du `value` nach `onValueChange` nicht aktualisierst, springt die Karte zurück. Das ist der Vertrag einer kontrollierten Komponente, und deshalb ist das Zurücksetzen oben nur ein `setTasks(previous)`. Der Snapshot ist allerdings nur sicher, solange immer nur ein Request läuft: Kommt eine zweite Verschiebung an, bevor die erste scheitert, macht das Wiederherstellen des ersten Snapshots auch die zweite rückgängig. Wenn sich Verschiebungen überschneiden können, lad das Board bei einem Fehler lieber neu.

## Wie funktioniert ein Kanban Board auf dem Smartphone?

Zeig immer nur eine Spalte. Ist das Board schmaler als 576px, nimmt jede Spalte 85% der Breite ein, die nächste schaut vom Rand herein, und das Board rastet von Spalte zu Spalte ein. Eine Reihe Chips oben zeigt jede Spalte mit ihrer Anzahl, hebt die sichtbare hervor und springt beim Antippen zu einer Spalte; `mobileNav={false}` blendet sie aus. Der Wechsel nutzt eine Container Query auf dem Board, also bekommt auch ein schmales Board in einem Seitenpanel am Desktop das Smartphone-Layout.

Touch-Drag startet nach 250ms langem Drücken, mit einem kurzen haptischen Tick, wo der Browser Vibration unterstützt. Ein schnelles Wischen scrollt also zwischen den Spalten, und erst bewusstes Halten hebt die Karte an. Hältst du eine angehobene Karte nahe am Rand des Boards, scrollt es seitwärts zur nächsten Spalte. Der ⋯-Button ist auf Touch-Geräten immer sichtbar, und „Move to“ öffnet sich als Bottom Sheet. Das Setup der Sensoren erklärt das [dnd kit Tutorial für Touch-Drag auf dem Smartphone](https://wingo-ui.com/de/blog/dnd-kit-mobile-touch-drag), das Muster für das Sheet der [Guide zum React Bottom Sheet](https://wingo-ui.com/de/blog/react-bottom-sheet-for-the-web).

Für ein Board, das bis an die Bildschirmränder reicht, gib die Full-Bleed-Klassen aus der Doku mit, damit die hereinschauende Spalte am Bildschirmrand beginnt:

```tsx
<Kanban columns={columns} value={deals} onValueChange={setDeals} classNames={{ nav: "-mx-4 px-4", board: "-mx-4 px-4 scroll-px-4" }} />
```

## Wie mache ich ein Kanban Board barrierefrei?

Gib jeder Verschiebung einen Weg per Tastatur, einen per Menü und eine gesprochene Ansage. Jede Karte ist ein Tab-Stopp. Die Leertaste hebt sie an, Pfeil hoch und runter bewegen sie in ihrer Spalte, Pfeil links und rechts springen an die nächstgelegene Stelle in der Nachbarspalte, Leertaste oder Enter legt sie ab, und Escape stellt sie zurück. Enter auf einer nicht angehobenen Karte führt `onCardClick` aus. Shift + F10 oder die Menütaste öffnet das ⋯-Menü der Karte, das auf dem [Dropdown Menu](https://wingo-ui.com/components/dropdown-menu) aufbaut; eigene Einträge ergänzt du mit `getCardActions`, Einträge für Spalten mit `getColumnActions`.

Mit Screenreader hörst du jeden Schritt als Live-Ansage, etwa „Northwind Logistics moved to Negotiation, position 2 of 4“. Alle Texte, auch diese Ansagen, stecken in `labels` und werden über `DEFAULT_KANBAN_LABELS` gelegt. So übersetzt du das Board, ohne es zu forken. Gib Karten, deren `title` kein String ist, einen `textValue`, damit Menüs und Ansagen reinen Text zum Vorlesen haben. Ist reduzierte Bewegung eingestellt, fallen das Kippen, das Pulsieren und das Gleiten weg.

## Was sind die typischen Fehler bei einem React Kanban Board?

Vier Fehler tauchen auf, sobald ein Board von Demodaten auf echte Daten umzieht.

- **Keine Höhe am Container.** Das Board füllt sein Elternelement. Ohne Höhe wachsen die Spalten mit ihren Karten, und die Seite scrollt statt der Spalte.
- **Eine Karte mit unbekannter columnId.** Sie wird nicht gerendert, und eine Warnung in der Entwicklung nennt sie. Prüf das, wenn die Spalten-IDs aus einer Datenbank kommen.
- **Ziehen als einziger Weg zum Verschieben.** Lass „Move to“ im Menü (es ist da, solange nicht `readOnly` gesetzt ist), sonst erfüllt das Board WCAG 2.5.7 nicht mehr.
- **Ein Board für eine lange Liste.** Eine Spalte mit Hunderten Karten liest sich besser als Tabelle mit Statusfilter, die auf dem Smartphone zu Karten wird ([Muster für responsive Tabellen](https://wingo-ui.com/de/blog/responsive-table-mobile-cards)).

## Wann nehme ich stattdessen die Deal Pipeline?

Wenn das Board eine Sales-Pipeline ist. Die [Deal Pipeline](https://wingo-ui.com/components/deal-pipeline) baut auf dem Kanban auf und ergänzt Wahrscheinlichkeiten pro Phase, Summe und gewichtete Summe pro Phase, eine Prognose mit der Abschlussquote und einen Dialog, der nach dem Grund fragt, wenn ein Deal auf Lost landet. `onDealMove` kann ein Promise zurückgeben: Die Karte zeigt beim Speichern einen Spinner und springt zurück, wenn das Speichern scheitert. Dazu kommen eine Listenansicht und ein Filter nach Verantwortlichen.

> Live-Demo (Deal Pipeline): Zieh einen Deal auf Lost: Ein Dialog fragt nach dem Grund, bevor die Verschiebung übernommen wird (auf dem Smartphone nutz ⋯ und dann Mark as lost). Wechsle in der Toolbar zu List, um dieselben Deals nach Phase gruppiert zu sehen. Probier sie unter [Deal Pipeline](https://wingo-ui.com/components/deal-pipeline) aus und installier sie mit `npx wingo-ui@latest add deal-pipeline`.

## Wie baue ich es in mein Projekt ein?

Führ `npx wingo-ui@latest add kanban` aus (oder `deal-pipeline`). Die CLI kopiert den Quellcode nach `components/ui`, ergänzt die Komponenten, Hooks und Helfer, die er importiert, und installiert `@dnd-kit/core`, `@dnd-kit/sortable`, `@dnd-kit/utilities`, `motion` und `lucide-react`. Beide sind Pro-Komponenten ([Preise](https://wingo-ui.com/de/pricing)). Weitere Deep Dives zu einzelnen Komponenten findest du in der [Kategorie Komponenten-Guides](https://wingo-ui.com/de/blog/category/components), und der [Guide zu React-Formularkomponenten](https://wingo-ui.com/de/blog/react-form-components-guide) wendet dieselben Regeln für Touch, Tastatur und Screenreader auf jedes Formularelement an.

## Komponenten in diesem Artikel

- [Kanban](https://wingo-ui.com/components/kanban) (Pro): A React kanban board for tasks, orders and pipelines: drag cards with mouse, touch or keyboard, WIP limits, inline add and a phone layout. Installation: `npx wingo-ui@latest add kanban`
- [Deal Pipeline](https://wingo-ui.com/components/deal-pipeline) (Pro): A React sales pipeline board: deal stages with weighted totals, a forecast summary, drag between stages with a lost reason, a list view. Installation: `npx wingo-ui@latest add deal-pipeline`

## Häufige Fragen

### Welche React Kanban Library ist die beste?

Das hängt davon ab, was du rund um das Board brauchst. Eine Quellcode-Komponente auf Basis von dnd kit, etwa das Kanban von Dice UI oder Wingo UI, gibt dir Code, den du ändern kannst; eine fertige Library wie SVAR React Kanban gibt dir ein konfiguriertes Widget. Prüf Touch-Drag, Verschieben per Tastatur und ein Smartphone-Layout, bevor du dich entscheidest, denn viele Boards zeigen ihre Demo nur am Desktop.

### Wie funktionieren WIP-Limits auf einem Kanban Board?

Ein WIP-Limit ist die Höchstzahl an Karten, die gleichzeitig in einer Spalte liegen sollen. Tools warnen entweder (Jira färbt eine Spalte rot, wenn sie ihr Maximum überschreitet) oder blockieren das Ablegen, bis eine Karte die Spalte verlässt; das Kanban von Wingo UI kann beides über limitBehavior="warn" oder "block".

### Wie funktioniert ein Kanban Board auf dem Smartphone?

Zeig immer nur eine Spalte, lass die nächste vom Rand hereinschauen, lass das Board zwischen den Spalten einrasten und ergänz eine Reihe Chips, um direkt zu einer Spalte zu springen. Heb Karten erst nach langem Drücken an, damit ein Wischen weiterhin scrollt, und biete für alle, die nicht ziehen wollen, ein Menü „Move to“ im Bottom Sheet an.

### Gibt es eine shadcn Kanban-Komponente?

shadcn/ui selbst liefert kein Kanban Board. Registries von Drittanbietern schon: Dice UI installierst du über die shadcn CLI, das Kanban von Wingo UI als Quellcode mit npx wingo-ui@latest add kanban.

### Ist das Kanban von Wingo UI kostenlos?

Nein. Kanban und Deal Pipeline sind Pro-Komponenten. Der Empty State, den es für leere Spalten nutzt, ist kostenlos.

---

Quelle: https://wingo-ui.com/de/blog/react-kanban-board
