React Swipe Cards: Ein Tinder-Stapel mit Undo

React Swipe Cards sind das Tinder-Muster außerhalb von Dating-Apps: ein Stapel Karten, eine Entscheidung nach der anderen, nach rechts wischen zum Behalten, nach links zum Überspringen. Teams nutzen das, um Spesen freizugeben, Leads zu sortieren, Lernkarten abzufragen oder auf dem Smartphone durch Mietwohnungen zu blättern. Demos findest du schnell. Schwierig sind drei Dinge: ein Drag, der auch auf einem Android-Mittelklassegerät flüssig bleibt, ein Undo, das auch das Speichern rückgängig macht, und ein Weg, ohne Wischen zu entscheiden. Dieses Tutorial baut die Karte zuerst von Grund auf mit motion und zeigt dann das Swipe Deck von Wingo UI, das Stempel, Stapel, Undo, Buttons und Tastaturbedienung ergänzt.
Was braucht eine gute Swipe-Card-Interaktion?
Sechs Dinge, ungefähr in der Reihenfolge, in der Leute sie bemerken:
- Die Karte folgt dem Finger und neigt sich. Der Drehpunkt liegt an der Unterkante, so wie eine Karte ausschwingt, die du unten festhältst.
- Ein Stempel zeigt, wohin es geht. Like oder Nope blendet sich auf der Seite ein, in deren Richtung die Karte unterwegs ist, mit Text und Icon, damit Farbe nie der einzige Hinweis ist.
- Eine klare Schwelle. Ab einem Viertel der Kartenbreite oder mit einem schnellen Flick entscheidet das Loslassen. Alles darunter federt zurück.
- Der Wurf behält sein Tempo. Nach einem kräftigen Flick fliegt die Karte schnell davon, nach einem langsamen Drag langsam. Sie trägt also den Schwung, den du ihr gibst.
- Die nächste Karte ist schon da. Die Karten dahinter rücken beim Ziehen nach, ihre Bilder sind bereits geladen.
- Undo, Buttons und Tasten. Leute wischen in die falsche Richtung, und manche können gar nicht wischen.
Miss jedes Paket für swipeable cards an dieser Liste. Zum Beispiel react-tinder-card (opens in a new tab), ein Web-Paket für den Tinder Swipe: Stand Oktober 2026 steht es bei Version 1.6.4 (veröffentlicht am 7. November 2023 (opens in a new tab), MIT). Es nennt React 16.8 bis 18 als Peer Dependencies, also meldet npm bei React 19 einen Peer-Konflikt. Es braucht @react-spring/web, bietet swipe(dir) und restoreCard() über einen Ref an, und die README erwähnt weder Tastaturbedienung noch Screenreader.
Wie baust du eine swipebare Karte in React von Grund auf?
Halte die Position in einem Motion Value und leite alles andere daraus ab. Eine swipebare Karte in React, die x im State speichert, rendert bei jeder Pointer-Bewegung neu, und auf einem Smartphone mit einem Foto in der Karte kommt genau daher das Ruckeln. Mit useMotionValue und useTransform aktualisieren sich Neigung und Stempel außerhalb von React, und die Komponente rendert einmal pro Karte:
Rendere sie mit key={item.id}, damit jede neue Karte bei null startet. Mit drag="x" setzt Motion touch-action: pan-y, ein vertikaler Swipe auf der Karte scrollt also weiterhin die Seite. Die 400px/s für den Flick und die Schwelle bei einem Viertel der Breite sind dieselben Zahlen, die lib/motion von Wingo UI für jeden Swipe, jedes Sheet und jeden Drawer der Bibliothek nutzt. Karte und Bottom Sheet lösen also an derselben Stelle aus.
Damit steht die Card Swipe Animation. Was dem Grundgerüst noch fehlt: die Karten dahinter, ein Wurf mit dem Tempo beim Loslassen (der Tween mit 0.3s ignoriert es), ein Klick, der feuert, wenn ein Drag auf einem Button in der Karte endet, ein haptischer Tick, sobald die Schwelle scharf ist, Undo, Buttons, Tasten und Ansagen. Um genau diese Liste geht es im Rest des Beitrags.
Wie baust du ein Swipe Deck mit Wingo UI?
Übergib items (jedes mit stabiler id), ein renderCard und ein onSwipe. Das Deck zeichnet die Kartenfläche, den Stapel, die Stempel, die runden Buttons und den Undo-Button. Hier ein Screen für Mietwohnungen: Merken behält ein Inserat, und die gemerkten sammeln sich in einem Grid unter dem Deck (das Grid SavedRentals kommt weiter unten in diesem Beitrag):
onSwipe läuft, sobald die Karte weg ist. Gibt der Handler ein Promise zurück, wartet das Deck darauf, bevor die nächste Entscheidung möglich ist, und ein Reject bringt die Karte mit dem Label error zurück („Couldn't save. Try again.“). Die Karten werden als stack gezeichnet (die hinteren lugen unten mit einer Skalierung von 0.95 und 0.9 hervor), als fan (sie neigen sich um 3 Grad nach beiden Seiten) oder als none. rotation legt die Neigung bei einem Drag über die volle Breite fest (standardmäßig 12 Grad, 0 hält sie aufrecht), und visibleCards bestimmt, wie viele gezeichnet werden, standardmäßig 3. So laden die nächsten Fotos schon, bevor sie oben ankommen.
$ npx wingo-ui@latest add swipe-deckWie funktioniert Undo in einem Swipe Deck?
Undo im Swipe Deck hat zwei Hälften: Die Karte kommt zurück, und was die Entscheidung gespeichert hat, wird rückgängig gemacht. Das Deck zählt die entschiedenen Karten in einem Index mit. Undo setzt ihn um eins zurück, und die Karte fliegt von der Seite herein, zu der sie verschwunden ist. Drei Wege lösen es aus: der kleine runde Pfeil neben den runden Buttons, Backspace und Cmd/Ctrl+Z. Jeder ruft onUndo(item, direction) auf, also machst du genau das rückgängig, was diese Entscheidung gespeichert hat; im Beispiel oben muss nur ein Merken zurückgenommen werden. showUndo={false} blendet den Button aus und schaltet die Tasten ab.
Willst du Undo aus deiner eigenen UI steuern, gibt dir setApi das Objekt { swipe(direction), undo(), index }. Und um das Deck von außen zu kontrollieren, übergib index und onIndexChange; Setzt du index auf 0, geht es von vorn los. So funktioniert ein Empty State mit einem Button „Noch einmal ansehen“.
Wie ergänzt du eine dritte Richtung?
Für eine Option „Später“ fügst du "up" zu directions hinzu. Ihr Stempel sitzt unten auf der Karte, und ihr runder Button ist kleiner und steht in der Mitte. Die meisten Apps sollten bei drei aufhören; diagonale Swipes oder Swipes nach unten sind schwer zu entdecken und leicht versehentlich auszulösen.
$ npx wingo-ui@latest add swipe-deckDie Richtung nach oben hat auf dem Smartphone ihren Preis. Mit nur links und rechts hat die Karte touch-action: pan-y, ein vertikaler Swipe, der auf der Karte beginnt, scrollt also weiterhin die Seite. Ist nach oben erlaubt, braucht die Karte auch vertikale Bewegungen und nutzt touch-action: none, die Seite lässt sich dann nicht mehr von der Karte aus scrollen. Lass über oder unter dem Deck Platz zum Scrollen, oder bleib auf scrollenden Screens bei zwei Richtungen. Unser Guide zu Tailwind touch-manipulation erklärt, welchen touch-action-Wert jede ziehbare Fläche braucht.
Wie lässt sich ohne Wischen entscheiden?
Mit den Buttons und Tasten, die dasselbe mit derselben Animation tun. Ein Swipe ist eine Ziehbewegung, und WCAG 2.2, Erfolgskriterium 2.5.7 (opens in a new tab) (Stufe AA) verlangt eine Single-Pointer-Alternative. Das Deck bietet:
- Runde 56px-Buttons unter dem Deck für jede Richtung (
showActions, standardmäßig an). Sie färben sich und wachsen, wenn ein Drag in ihre Richtung geht, und bringen so nebenbei die Geste bei. - Tasten. Spring mit Tab auf die Karte, dann entscheiden die Pfeiltasten links und rechts (und oben, wenn erlaubt). Backspace oder Cmd/Ctrl+Z machen rückgängig. Der Fokus wandert zur nächsten Karte und nach der letzten zum Empty State.
- Ansagen. Die oberste Karte trägt Hinweise für Screenreader („Swipe or use Left and Right arrows to decide. Backspace undoes.“), und jede Entscheidung wird mit dem Rest angesagt, etwa „Hilton Boston, $412.00: Accept. 2 left.“, wenn du
getItemLabelsetzt. - Klickbare Inhalte. Buttons, Links und Eingabefelder in einer Karte starten nie einen Drag, ein Button „Details ansehen“ in der Karte funktioniert also weiterhin.
- Reduzierte Bewegung. Die Karte neigt sich nicht und verschwindet mit einem kurzen Fade und Slide.
Auf Smartphones gibt es einen mittleren haptischen Tick, sobald der Drag scharf wird, einen leichten beim Auslösen und ein Fehlermuster, wenn das Speichern scheitert. Sieh das als Bonus: Stand Oktober 2026 zeigt caniuse (opens in a new tab) kein navigator.vibrate in Safari. Unser Guide zur Web Vibration API erklärt die Lücken.
Wo passt eine Listing Card zu Swipe Cards?
Nach dem Deck, nicht darin. Eine Karte im Deck sollte eine einzige große Drag-Fläche sein. Die Listing Card ist für Grids gebaut: Ihre Fotos sind ein Karussell, durch das du seitlich wischst, und Titel und Fotos sind Links. In einem Deck konkurrieren diese Gesten mit dem Karten-Swipe, und das Deck startet auf einem Link keinen Drag. Nutz also das Deck zum Entscheiden und die Listing Card, um zu zeigen, was du behalten hast, in einem zweispaltigen Grid fürs Smartphone:
size="sm" ist die Größe für zweispaltige Grids auf dem Smartphone. favorite: true startet jedes Herz gefüllt, und mit onFavoriteChange können Leute im Grid ein Inserat wieder entfernen und es erneut merken, wenn sie es sich anders überlegen: Gib das Promise zurück, dann bleibt das Herz im neuen Zustand, solange das Promise offen ist, oder springt zurück, wenn der Request scheitert.
$ npx wingo-ui@latest add listing-cardWelche Fehler bei Swipe Cards tauchen in Produktion auf?
- State pro Frame.
setStateinonDragrendert die Karte bei jeder Bewegung neu. Halte Bewegung in Motion Values; das Deck setzt State nur, wenn eine Richtung scharf wird oder eine Karte entschieden ist. - Eine Karte, die der Seite entkommt. Eine geworfene Karte fliegt über die Ränder des Decks hinaus und kann eine horizontale Scrollbar erzeugen. Pack den Screen in
overflow-x-clip. - Speichern, bevor die Karte weg ist. Speichere in
onSwipe, denn das läuft erst, wenn die Karte weg ist, und gib das Promise zurück, damit ein Fehler sie zurückbringt, statt die Entscheidung zu verlieren. - Undo, das nur die Karte bewegt. Hat Merken auf den Server geschrieben, muss Undo das zurücknehmen.
onUndogibt dir genau dafür das Item und die Richtung. - Stempel, die sich auf Farbe verlassen. Grüne und rote Stempel allein funktionieren für farbenblinde Menschen nicht. Behalte Text und Icon.
Swipe-Zeilen sind die kleinen Verwandten der Swipe Cards; unser Tutorial zu React Swipe to Delete nutzt dieselben Motion Values für Listenzeilen. Für alles Weitere rund um mobile UI lies unseren Guide zu Mobile-first React-Komponenten oder stöbere in Mobile UI in React.
Was solltest du als Nächstes installieren?
npx wingo-ui@latest add swipe-deck kopiert das Swipe Deck samt Button, Empty State und den Helfern für Haptik, Motion und State, die es importiert, und installiert motion und lucide-react. Für das Grid der gemerkten Inserate ergänzt du die Listing Card mit npx wingo-ui@latest add listing-card. Beide gehören zu Wingo UI Pro, melde dich also zuerst mit npx wingo-ui@latest login an.
Häufige Fragen
Wie baue ich Swipe Cards im Tinder-Stil in React?
Mach die oberste Karte horizontal ziehbar, halte ihre Position in einem Motion Value, leite Rotation und die Stempel Like und Nope mit useTransform aus diesem Wert ab und wirf die Karte beim Loslassen entweder aus dem Bild oder lass sie zurückfedern. Render die nächsten Karten dahinter, damit immer eine bereitliegt.
Warum ruckeln meine Swipe Cards auf dem Smartphone?
Meist, weil die Drag-Position im React State liegt, der die Karte und alles darin bei jeder Pointer-Bewegung neu rendert. Halte die Position in einem Motion Value oder einem Ref und setz State erst, wenn die Karte entschieden ist.
Funktioniert react-tinder-card mit React 19?
Das neueste Release, 1.6.4 vom November 2023, nennt React 16.8 bis 18 als Peer Dependencies, also meldet npm bei React 19 einen Peer-Konflikt. Außerdem braucht es @react-spring/web, und die README erwähnt keine Tastaturbedienung.
Wie ergänze ich Undo in einem Swipe Deck?
Merk dir den Index der obersten Karte und die Richtung jeder Entscheidung. Undo verringert den Index, macht rückgängig, was die Entscheidung gespeichert hat, und animiert die Karte von der Seite zurück, zu der sie hinausgeflogen ist. Das Swipe Deck von Wingo UI macht das per Button, Backspace oder Cmd/Ctrl+Z und ruft onUndo mit der Karte und ihrer Richtung auf.
Sind Swipe Cards barrierefrei?
Nur mit Buttons oder Tasten für jede Richtung, denn ein Swipe ist laut WCAG 2.2, Erfolgskriterium 2.5.7, eine Ziehbewegung. Das Deck sollte außerdem jede Entscheidung ansagen und den Fokus auf die nächste Karte setzen.
- Mobile UI
- Swipe Cards
- Gesten
- Motion
- React


