# dnd kit Mobile: Touch-Drag, ohne das Scrollen zu blockieren

> dnd kit mobile reparieren: TouchSensor mit Long Press, bei dem die Seite weiter scrollt, touch-action pro Element, Resize-Abbruch in iOS Safari und ein Tap-Weg.

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

## Kurz gesagt

Damit dnd kit mobile Drag funktioniert, ohne das Scrollen der Seite zu blockieren, ersetzt du die Standard-Sensoren durch einen Pointer-Sensor, der Touch ignoriert, einen TouchSensor mit 250ms Delay und 5 bis 8px Toleranz und einen KeyboardSensor, und setzt touch-action: manipulation auf Flächen mit Long Press und touch-action: none nur auf Drag-Handles. Halte den Drag in einem eigenen Scroll-Container, denn @dnd-kit/core bricht einen Drag bei einem Resize des Fensters ab, und iOS Safari ändert die Fenstergröße, wenn die Toolbar einklappt. Ergänze einen Tap-Weg, etwa ein Menü „Move to“ oder Buttons nach oben und unten, denn Drag per Tastatur allein erfüllt WCAG 2.5.7 nicht.

Du hast mit dnd kit eine sortierbare Liste oder ein Kanban-Board gebaut. Mit der Maus läuft alles, auf dem Smartphone klappt nichts mehr: Die Seite scrollt, während die Karte stehen bleibt, oder die Karte schnappt sich jedes Wischen, und die Liste scrollt nie. Dieses Tutorial bringt den dnd kit Mobile-Drag Schritt für Schritt in Ordnung: welche Sensoren du registrierst, der Kompromiss zwischen Aktivierungs-Delay und Scrollen, wohin `touch-action` gehört, warum iOS Safari Drags abbricht und welche Wege per Tastatur und Tap Drag and Drop barrierefrei machen. Das Sensor-Setup ist das aus dem [Kanban](https://wingo-ui.com/components/kanban) von Wingo UI, auf dem die [Deal Pipeline](https://wingo-ui.com/components/deal-pipeline) aufbaut; die [List](https://wingo-ui.com/components/list) zeigt die Variante mit Drag-Handle.

Stand Oktober 2026 ist die neueste Version von `@dnd-kit/core` noch 6.3.1 vom Dezember 2024, und die [Legacy-Doku](https://dndkit.com/legacy/introduction/installation/) empfiehlt die neue Version von dnd kit (`@dnd-kit/react` für React), die bei 0.5.0 steht, also vor 1.0. Dieser Artikel zielt auf `@dnd-kit/core` 6.3.1 und `@dnd-kit/sortable` 10.0.0.

## Warum funktioniert dnd kit auf dem Smartphone nicht?

Weil `DndContext` standardmäßig den PointerSensor und den KeyboardSensor nutzt, und Pointer Events ein Scrollen nicht stoppen können. Bewegt sich ein Finger auf einem Element, das Panning erlaubt, beginnt der Browser zu scrollen und feuert `pointercancel`. Das beendet den Drag, und `preventDefault()` in `pointermove` ändert daran nichts. Die [Doku zum Pointer-Sensor von dnd kit](https://dndkit.com/legacy/api-documentation/sensors/pointer) sagt es klar: `touch-action: none;` ist dort der einzige verlässliche Weg, das Scrollen bei Pointer Events zu verhindern.

Damit bleiben zwei Werkzeuge, beide mit einem Haken:

- **`touch-action: none` auf dem Draggable.** Der Drag funktioniert, aber ein Wischen, das auf diesem Element beginnt, kann die Seite nie scrollen. Für einen kleinen Griff in Ordnung, schlecht für eine Liste aus Zeilen in voller Breite: Steht jede Zeile auf `none`, scrollt kein Wischen auf der Liste. Die Doku schlägt `none` deshalb nur für einen Drag-Handle vor.
- **Der TouchSensor.** Touch Events lassen sich in `touchmove` abbrechen, also kann ein Drag auf einen Long Press warten und trotzdem ein Wischen scrollen lassen. Dieselbe Doku-Seite empfiehlt die Mouse- und Touch-Sensoren immer dann, wenn `none` nicht zu deinem Layout passt.

Das Problem ist alt: [Issue #435](https://github.com/clauderic/dnd-kit/issues/435) vom August 2021 beschreibt, dass Drag mit dem PointerSensor auf Touch-Geräten schlecht funktioniert. Wenn die Zeilen selbst scrollen müssen, braucht React Drag and Drop auf Touch-Geräten den TouchSensor, neben einem Sensor für die Maus.

## Welche dnd kit Sensoren funktionieren auf einem Touchscreen?

Drei: ein Pointer-Sensor, der Finger ignoriert, der dnd kit Touch Sensor mit Delay und der Keyboard-Sensor. Die Falle ist, den Standard-PointerSensor neben dem TouchSensor zu registrieren. Ein Touch feuert `pointerdown` vor `touchstart`, und `DndContext` ignoriert den Activator eines Sensors, solange ein anderer Sensor noch aussteht; der Check im Quelltext von 6.3.1 trägt einen Kommentar, dass gerade schon ein anderer Sensor instanziiert wird. Der PointerSensor nimmt also jeden Finger, und das Delay des TouchSensor läuft nie.

Wir haben es mit `@dnd-kit/core` 6.3.1 in headless Chromium mit emuliertem Touch getestet: ein Draggable auf einer Seite, die höher als der Bildschirm ist, 8px Distanz am Pointer-Sensor, 250ms und 8px am TouchSensor, und ein Finger, der sich 60px bewegt, entweder sofort oder nach 400ms Halten.

| Sensoren | `touch-action` | Sofort wischen | 400ms halten, dann bewegen |
| --- | --- | --- | --- |
| PointerSensor + TouchSensor | `manipulation` | Drag startet, bricht dann ab, weil die Seite scrollt | dasselbe: kein Long Press hebt es je an |
| PointerSensor + TouchSensor | `none` | Drag startet nach 8px, die Seite kann nicht scrollen | Drag startet nach 8px |
| Pointer-Sensor, der Touch überspringt + TouchSensor | `manipulation` | die Seite scrollt, kein Drag | Drag startet nach 250ms, die Seite bleibt stehen |

Ein verbreiteter Workaround wählt einen Sensor nach Gerätetyp. Das scheitert auf Laptops mit Touchscreen, wo dieselbe Person Trackpad und Bildschirm nutzt. Die letzte Zeile ist die sauberere Lösung: eine Unterklasse von PointerSensor, deren Activator Touch-Pointer überspringt. So nehmen Maus und Stift den Pointer-Weg und Finger den Touch-Weg. Unser Kanban nutzt dieselben Pointer- und Touch-Sensoren. Speicher das als `components/use-drag-sensors.ts`; das komplette Beispiel am Ende importiert es:

```ts
"use client";

import { KeyboardSensor, PointerSensor, TouchSensor, useSensor, useSensors } from "@dnd-kit/core";
import { sortableKeyboardCoordinates } from "@dnd-kit/sortable";

// PointerSensor nur für Maus und Stift: Finger übernimmt der TouchSensor
export class MousePointerSensor extends PointerSensor {
  static activators = [
    {
      eventName: "onPointerDown" as const,
      handler: ({ nativeEvent: event }: { nativeEvent: PointerEvent }) =>
        event.isPrimary && event.button === 0 && event.pointerType !== "touch",
    },
  ];
}

export function useDragSensors() {
  return useSensors(
    // Maus und Stift: 8px Weg, damit ein Klick ein Klick bleibt
    useSensor(MousePointerSensor, { activationConstraint: { distance: 8 } }),
    // Touch: 250ms drücken, damit ein Wischen weiter scrollt
    useSensor(TouchSensor, { activationConstraint: { delay: 250, tolerance: 8 } }),
    // Tastatur: Leertaste oder Enter hebt an, Pfeile bewegen, Leertaste oder Enter legt ab, Escape bricht ab
    useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }),
  );
}
```

Die Kombination aus Standard-`MouseSensor` und `TouchSensor` aus der Doku funktioniert ebenfalls.

## Welches Delay und welche Toleranz sollte der TouchSensor haben?

250ms mit 5 bis 8px Toleranz. Das Delay ist die Zeit, die ein Finger ruhen muss, bevor die Karte sich hebt. Die Toleranz ist die Strecke, die er in dieser Zeit driften darf, bevor dnd kit aufgibt und den Browser scrollen lässt. In diesen beiden Zahlen steckt der ganze Kompromiss zwischen dnd kit mobile Scroll und Drag:

| Einstellung | Zu niedrig | Zu hoch |
| --- | --- | --- |
| `delay` | ein langsames Wischen hebt eine Karte an, statt zu scrollen | der Drag fühlt sich kaputt an und kollidiert mit dem Long Press des Systems (Textauswahl, das iOS-Callout) |
| `tolerance` | ein ruhender Finger driftet ein Pixel, und der Drag startet nie | ein langsames Scrollen kann mittendrin zum Drag werden |

Nimm nie eine Toleranz von null. Laut [Doku zum Touch-Sensor](https://dndkit.com/legacy/api-documentation/sensors/touch) bricht mit null jede Bewegung während des Delays den Drag ab, und ein Finger auf Glas bewegt sich immer ein wenig. [Issue #1955](https://github.com/clauderic/dnd-kit/issues/1955), im März 2026 gegen die Neufassung eröffnet, meldete Drags, die auf manchen Android-Smartphones nie starteten, vor allem auf Samsung-Geräten; im Juli 2026 fand der Autor des Issues die Ursache in einem `tolerance: 0` und behob sie mit 5. Der PointerSensor der Neufassung gibt Touch standardmäßig [250ms Delay mit 5px Toleranz](https://dndkit.com/extend/sensors/pointer-sensor); unser Kanban und die Deal Pipeline nutzen 250ms und 8px.

Ein Long Press braucht Feedback genau in dem Moment, in dem er auslöst, sonst lassen Leute zu früh los. Das Kanban ruft in `onDragStart` `navigator.vibrate(10)` auf, wenn das Activator-Event ein `TouchEvent` ist, und hebt die Karte mit einer kleinen Skalierung und 2 Grad Neigung per Spring-Animation an. Auf dem iPhone bewirkt der Tick nichts, denn Safari auf iOS hat keine Vibration API ([caniuse](https://caniuse.com/vibration), geprüft im Oktober 2026; was ein iPhone stattdessen kann, steht im [Guide zur Web Vibration API](https://wingo-ui.com/de/blog/web-vibration-api-haptic-feedback)).

## Wohin gehört touch-action?

Auf das Element, das die Listener bekommt, mit einem Wert, der davon abhängt, welcher Sensor den Finger übernimmt. Die Doku empfiehlt `manipulation` für den TouchSensor und `none` für den PointerSensor. Der Browser liest den Wert, wenn der Touch beginnt: Eine Änderung nach `pointerdown` oder `touchstart` wird ignoriert. Schreib ihn also ins CSS, nie in einen Handler für den Drag-Start.

| Drag-Fläche | Sensor, der den Finger übernimmt | `touch-action` | Tailwind v4 |
| --- | --- | --- | --- |
| Die ganze Karte oder Zeile, Long Press | TouchSensor | `manipulation` | `touch-manipulation` |
| Ein Griff-Button | egal welcher | `none` | `touch-none` |
| Der Scroll-Container | kein Sensor | auf `auto` lassen | nichts |

`manipulation` behält Panning und Pinch-Zoom und schaltet den Zoom per Doppeltipp ab; sobald der Drag aktiv ist, bricht der TouchSensor `touchmove` selbst ab. Eine Fläche mit Long Press braucht zwei weitere Klassen: `select-none`, damit der Druck nicht den Text der Karte markiert, und `[-webkit-touch-callout:none]`, damit iOS sein Callout nicht öffnet. dnd kit bricht `contextmenu` auf dem Window schon selbst ab, solange ein Touch aussteht oder ein Drag läuft.

Die List von Wingo UI geht den Weg mit Griff: ein 32px-Button mit `touch-none`, dessen Trefferfläche bei groben Pointern auf 44px wächst ([warum 44px](https://wingo-ui.com/de/blog/minimum-touch-target-size-react-tailwind)). Ein Wischen auf der Zeile scrollt also weiter die Seite oder öffnet ihre Swipe-Aktionen.

## Warum bricht ein Drag auf dem iPhone ab, wenn die Seite scrollt?

Weil `@dnd-kit/core` jeden Pointer- oder Touch-Drag bei einem `resize` des Fensters abbricht, und Safari auf iOS `resize` feuert, wenn die Toolbar beim Scrollen des Dokuments ein- oder ausklappt. [Issue #686](https://github.com/clauderic/dnd-kit/issues/686) hat das im März 2022 unter iOS 15 eingegrenzt. Geschlossen wurde es im Februar 2026, weil es gegen die alte Version gemeldet war, nicht weil es behoben wurde: Der `resize`-Listener steckt noch in den Sensoren von 6.3.1, der Abbruch passiert also weiterhin.

Die Lösung liegt im Layout: Halte den Drag in einem eigenen Scroll-Container, damit sich das Dokument nicht bewegt. Gib dem Board oder der Liste eine echte Höhe (`h-dvh`, oder `flex-1 min-h-0` in einem Layout mit voller Höhe; siehe [100vh auf dem Smartphone](https://wingo-ui.com/de/blog/100vh-mobile-dvh-tailwind)), `overflow-y-auto` für eine Liste oder `overflow-x-auto` für ein Board, und `overscroll-contain`, damit das Scrollen am Ende nicht auf die Seite übergeht.

Der Container entscheidet auch, was Auto-Scroll bewegt. dnd kit scrollt die scrollbaren Vorfahren des Elements, also Elemente mit `overflow` auf `auto` oder `scroll`, plus das Dokument, sobald der Pointer weniger als 20% von ihrem Rand entfernt ist. Ein Wrapper mit `overflow: hidden` zählt nicht. Ein Board, das so abgeschnitten wird, kann also nicht zu seinen versteckten Spalten scrollen; stattdessen scrollt die Seite, falls sie kann. Nutzt ein Board auf dem Smartphone Scroll Snap, pausier es während des Drags: Das Kanban-Board ist `snap-x snap-mandatory` mit `data-dragging:snap-none`, damit der Auto-Scroll zur nächsten Spalte gleiten kann.

Probier es in Smartphone-Breite aus (oder im Device Mode): Das Kanban zeigt jeweils eine Spalte, die nächste lugt hervor, ein Wischen scrollt, und 250ms Drücken hebt eine Karte an, die du an den Rand tragen kannst, um die nächste Spalte zu erreichen.

> Live-Demo (Kanban): Drück lange auf eine Karte, um sie anzuheben, und trag sie dann an den Rand des Boards, um die nächste Spalte zu erreichen; ein schnelles Wischen scrollt weiter. Mit der Maus hebt sie sich nach 8px. Geh per Tab zu einer Karte und drück die Leertaste, um sie mit den Pfeiltasten zu bewegen. Probier sie unter [Kanban](https://wingo-ui.com/components/kanban) aus und installier sie mit `npx wingo-ui@latest add kanban`.

## Wie machst du Drag and Drop barrierefrei?

Mit zwei getrennten Wegen, und einen davon liefert dnd kit mit. Der KeyboardSensor deckt Tastaturnutzer ab: Standardmäßig hebt Leertaste oder Enter an, die Pfeiltasten bewegen, Leertaste, Enter oder Tab legen ab und Escape bricht ab, während `DndContext` Anleitungen und Live-Ansagen an Screenreader gibt. Schreib eigene `announcements`, denn die Standardtexte lesen rohe IDs vor (`Picked up draggable item d02.`). Das Kanban übergibt `keyboardCodes: { start: ["Space"], cancel: ["Escape"], end: ["Space", "Enter"] }`, weil Enter eine Karte öffnet, und sein eigener `coordinateGetter` schickt Links und Rechts zur nächstgelegenen Karte in der nächsten Spalte, die sie annimmt.

Der zweite Weg ist für Menschen, die tippen, aber nicht ziehen können, wegen eines Tremors, eines Trackballs oder einer Kopfmaus. Das WCAG-2.2-Erfolgskriterium [2.5.7 Dragging Movements](https://www.w3.org/WAI/WCAG22/Understanding/dragging-movements.html) (Stufe AA) verlangt, dass sich jede Ziehbewegung auch mit einem einzelnen Pointer ohne Ziehen erledigen lässt, und die Erläuterung des W3C stellt klar, dass eine Tastaturalternative allein dafür nicht reicht. Eines ihrer Beispiele ist genau das Muster, das eine Liste braucht: Nach einem Tap auf ein Element verschieben Bedienelemente daneben das Element nach oben oder unten. Bei einem Board übernimmt das ein Menü, das die Zielspalte nennt. Das Kanban setzt „Move to“ an die erste Stelle im ⋯-Menü jeder Karte, das sich auf Smartphones als Bottom Sheet öffnet.

## Wie baust du eine sortierbare Liste, die auf Smartphones funktioniert?

Kombinier alles bisher Gezeigte in einer Komponente: den Hook `useDragSensors`, Zeilen mit Long Press, eine Liste mit eigenem Scroller, benannte Ansagen und Buttons nach oben und unten für den Tap-Weg. Sie übergibt außerdem `id={useId()}` an `DndContext`. Ohne das nummeriert dnd kit seine `aria-describedby`-IDs mit einem Zähler auf Modulebene, Server und Browser können unterschiedlich zählen, und React meldet in Next.js einen Hydration Mismatch.

Installier die Pakete mit `npm i @dnd-kit/core @dnd-kit/sortable @dnd-kit/utilities lucide-react` und den kostenlosen [Button](https://wingo-ui.com/components/button) mit `npx wingo-ui@latest add button`. Speicher das dann als `components/sortable-tasks.tsx`, neben `use-drag-sensors.ts`:

```tsx
"use client";

import { useId, useState } from "react";
import { DndContext, closestCenter, type Announcements, type DragEndEvent, type UniqueIdentifier } from "@dnd-kit/core";
import { SortableContext, arrayMove, useSortable, verticalListSortingStrategy } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { ArrowDown, ArrowUp } from "lucide-react";
import { Button } from "@/components/ui/button";
import { useDragSensors } from "./use-drag-sensors";

type Task = { id: string; title: string };

export function SortableTasks({ initial }: { initial: Task[] }) {
  const [tasks, setTasks] = useState(initial);
  const sensors = useDragSensors();
  // eine stabile ID: der eigene Zähler von dnd kit unterscheidet sich zwischen Server und Browser
  const dndId = useId();

  const name = (id: UniqueIdentifier) => tasks.find((task) => task.id === id)?.title ?? "Aufgabe";
  const place = (id: UniqueIdentifier) => `Position ${tasks.findIndex((task) => task.id === id) + 1} von ${tasks.length}`;
  const announcements: Announcements = {
    onDragStart: ({ active }) => `${name(active.id)} angehoben.`,
    onDragOver: ({ active, over }) => (over ? `${name(active.id)} ist auf ${place(over.id)}.` : undefined),
    onDragEnd: ({ active, over }) => (over ? `${name(active.id)} auf ${place(over.id)} abgelegt.` : `${name(active.id)} ist wieder an seinem Platz.`),
    onDragCancel: ({ active }) => `Abgebrochen. ${name(active.id)} ist wieder an seinem Platz.`,
  };

  const move = (from: number, to: number) => {
    if (to < 0 || to >= tasks.length || from === to) return;
    setTasks((current) => arrayMove(current, from, to));
  };

  const onDragEnd = ({ active, over }: DragEndEvent) => {
    if (!over) return;
    move(tasks.findIndex((task) => task.id === active.id), tasks.findIndex((task) => task.id === over.id));
  };

  return (
    <DndContext
      id={dndId}
      sensors={sensors}
      collisionDetection={closestCenter}
      onDragEnd={onDragEnd}
      accessibility={{
        announcements,
        screenReaderInstructions: {
          draggable: "Drück die Leertaste, um eine Aufgabe anzuheben. Die Pfeiltasten bewegen sie, die Leertaste legt sie ab, Escape bricht ab.",
        },
      }}
    >
      <SortableContext items={tasks} strategy={verticalListSortingStrategy}>
        {/* ein eigener Scroller: der Drag scrollt diese Liste, nicht das Dokument, also bleibt die Toolbar von Safari stehen */}
        <ul className="flex max-h-[70dvh] flex-col gap-2 overflow-y-auto overscroll-contain p-1">
          {tasks.map((task, index) => (
            <TaskRow key={task.id} task={task} index={index} last={index === tasks.length - 1} onMove={move} />
          ))}
        </ul>
      </SortableContext>
    </DndContext>
  );
}

type TaskRowProps = { task: Task; index: number; last: boolean; onMove: (from: number, to: number) => void };

function TaskRow({ task, index, last, onMove }: TaskRowProps) {
  const { attributes, listeners, setNodeRef, setActivatorNodeRef, transform, transition, isDragging } = useSortable({
    id: task.id,
  });

  return (
    <li
      ref={setNodeRef}
      style={{ transform: CSS.Transform.toString(transform), transition }}
      data-dragging={isDragging || undefined}
      className="flex items-center gap-1 rounded-2xl bg-card ps-4 pe-1 shadow-raised data-dragging:relative data-dragging:z-10 data-dragging:shadow-floating"
    >
      {/* das Label ist die Drag-Fläche: ein Wischen scrollt die Liste, 250ms Drücken hebt die Zeile an */}
      <div
        ref={setActivatorNodeRef}
        {...attributes}
        {...listeners}
        className="flex min-h-12 flex-1 cursor-grab touch-manipulation select-none items-center text-sm outline-hidden [-webkit-tap-highlight-color:transparent] [-webkit-touch-callout:none] focus-visible:outline-2 focus-visible:outline-solid focus-visible:outline-ring"
      >
        {task.title}
      </div>
      {/* dieselbe Bewegung mit einzelnen Taps (WCAG 2.5.7) */}
      <Button variant="ghost" size="icon" radius="full" aria-label={`${task.title} nach oben verschieben`} disabled={index === 0} onClick={() => onMove(index, index - 1)}>
        <ArrowUp />
      </Button>
      <Button variant="ghost" size="icon" radius="full" aria-label={`${task.title} nach unten verschieben`} disabled={last} onClick={() => onMove(index, index + 1)}>
        <ArrowDown />
      </Button>
    </li>
  );
}
```

Eine Server-Seite wie `app/tasks/page.tsx` kann sie mit einfachen Daten rendern:

```tsx
import { SortableTasks } from "@/components/sortable-tasks";

const tasks = [
  { id: "t1", title: "Den Mietvertrag für New York an Emma Carter schicken" },
  { id: "t2", title: "Den Fotografen für Freitag buchen" },
  { id: "t3", title: "Das Q4-Budget prüfen" },
];

export default function TasksPage() {
  return <SortableTasks initial={tasks} />;
}
```

Teste dann auf einem echten Smartphone: Der Device Mode zeigt weder die Toolbar von Safari noch die Drift eines echten Fingers oder den Long Press des Systems. Probier außerdem jeden Weg mit eingeschaltetem Screenreader aus.

Wenn du diesen Code für eine einfache Liste lieber nicht selbst pflegen willst: Die List hat sortierbare Zeilen eingebaut. Schalte `sortable` ein, dann zieh eine Zeile an ihrem Griff, oder geh per Tab zu einem Griff und nutz Leertaste und Pfeiltasten; Alt mit Pfeil nach oben oder unten verschiebt eine fokussierte Zeile um eine Stelle. Einen Tap-Weg zum Umsortieren hat sie nicht. Für 2.5.7 ergänzt du deshalb Einträge „Move up“ und „Move down“ im `actions`-Menü jeder Zeile (ihr ⋯-Button ist auf Touch-Geräten immer sichtbar) und sortierst deine `items` in deren Handlern um.

> Live-Demo (List): Zieh eine Zeile an ihrem Griff, um die Inbox umzusortieren; ein Wischen auf der Zeile scrollt weiter. Geh per Tab zu einem Griff und drück die Leertaste, dann die Pfeiltasten, oder drück Alt mit Pfeil nach oben oder unten auf einer fokussierten Zeile. Probier sie unter [List](https://wingo-ui.com/components/list) aus und installier sie mit `npx wingo-ui@latest add list`.

## Gibt es ein React Kanban Board, das schon auf Smartphones funktioniert?

Ja: Das Kanban von Wingo UI liefert das Setup für Touch, Maus und Tastatur aus diesem Artikel, das Menü „Move to“ und das Smartphone-Layout mit. Der [Guide zum React Kanban Board](https://wingo-ui.com/de/blog/react-kanban-board) behandelt seine WIP-Limits, den kontrollierten Wert und das einspaltige Layout auf dem Smartphone. `npx wingo-ui@latest add kanban` kopiert es samt allem, was es importiert, in dein Projekt und installiert seine npm-Pakete. Kanban, List und Deal Pipeline sind Pro-Komponenten ([Preise](https://wingo-ui.com/de/pricing)). Für die anderen Gesten auf dem Smartphone, etwa Swipe-Aktionen und Ziehen zum Schließen, lies den [Guide zu Mobile-first React-Komponenten](https://wingo-ui.com/de/blog/mobile-first-react-components). 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, und weitere Tutorials wie dieses findest du in der [Kategorie Komponenten-Guides](https://wingo-ui.com/de/blog/category/components).

## 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`
- [List](https://wingo-ui.com/components/list) (Pro): A React list for settings, contacts, inboxes and orders: iOS grouped panels, selection, row menus, swipe actions and drag to reorder. Installation: `npx wingo-ui@latest add list`
- [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

### Warum funktioniert dnd kit auf dem Smartphone nicht?

DndContext nutzt standardmäßig den PointerSensor, und Pointer Events können den Browser nicht am Scrollen hindern. Ein Touch-Drag wird also abgebrochen, sobald die Seite anfängt zu scrollen. Setz entweder touch-action: none auf einen Drag-Handle, oder lass einen TouchSensor mit 250ms Delay den Finger übernehmen, neben einem Pointer-Sensor, der Touch überspringt: Dann hebt ein Long Press das Element an und ein Wischen scrollt weiter.

### Kann ich PointerSensor und TouchSensor in dnd kit zusammen nutzen?

Nicht den Standard-PointerSensor. Ein Touch feuert pointerdown vor touchstart, also schnappt sich der PointerSensor den Finger, und das Delay des TouchSensor läuft nie. Leite eine Klasse von PointerSensor ab, deren Activator, pointerType touch überspringt, oder kombinier MouseSensor mit TouchSensor.

### Welches Delay und welche Toleranz sollte der TouchSensor von dnd kit haben?

Starte mit 250ms Delay und 5 bis 8px Toleranz; die Neufassung von dnd kit nutzt 250ms und 5px als Standard für Touch. Setz die Toleranz nie auf 0, denn laut Doku bricht dann jede Bewegung während des Delays den Drag ab, und ein ruhender Finger driftet immer ein wenig.

### Warum bricht dnd kit meinen Drag auf dem iPhone ab?

@dnd-kit/core bricht Pointer- und Touch-Drags bei einem Resize des Fensters ab, und Safari auf iOS feuert resize, wenn die Toolbar beim Scrollen ein- oder ausklappt. Halte den Drag in einem eigenen Scroll-Container, damit das Dokument während eines Drags nie scrollt.

### Reicht Drag and Drop per Tastatur für Barrierefreiheit?

Es deckt Tastaturnutzer ab, aber nicht das WCAG-2.2-Erfolgskriterium 2.5.7 Dragging Movements, das einen Weg verlangt, dasselbe mit einzelnen Taps oder Klicks zu erledigen. Ergänze neben dem Drag ein Menü „Move to“ oder Buttons nach oben und unten.

### Sollte ich für Touch-Drag auf @dnd-kit/react umsteigen?

Nicht nur wegen Touch. Die Legacy-Doku verweist auf die neue Version, und ihr PointerSensor gibt Touch standardmäßig 250ms Delay mit 5px Toleranz, aber Stand Oktober 2026 steht @dnd-kit/react bei 0.5.0, vor 1.0, mit einer anderen API. Auf @dnd-kit/core 6.3.1 behebt das Sensor-Setup aus diesem Artikel den Touch-Drag.

---

Quelle: https://wingo-ui.com/de/blog/dnd-kit-mobile-touch-drag
