Embla Carousel React: barrierefreie Slides ohne Layout Shift

Du brauchst eine Fotogalerie, eine Leiste mit Produktkarten, Testimonials oder Willkommens-Slides, und du hast Embla gewählt, weil es sich auf dem Smartphone gut ziehen lässt. Der Hook von Embla Carousel React liefert dir Drag-Physik und Einrasten mit ein paar Zeilen Markup. Danach fängt die eigentliche Arbeit an: Pfeile, Dots, Tastaturbedienung, Autoplay, das man stoppen kann, Slides, die bei der Hydration nicht springen, und Screenreader, die „3 of 8“ hören statt eines Haufens Bilder. Dieser Guide zeigt, was Embla dir gibt, was du noch selbst bauen musst und wie das Carousel von Wingo UI jeden dieser Punkte löst.
Was macht Embla Carousel in React?
Es verschiebt den Track, um alles andere kümmerst du dich. Embla ist eine Karussell-Engine ohne Abhängigkeiten (opens in a new tab) unter MIT-Lizenz. Dieser Guide nutzt Embla 8, die stabile Versionsreihe (8.6.0, Stand Oktober 2026, Doku zu v8 (opens in a new tab)). Das React-Paket stellt einen einzigen Hook bereit, useEmblaCarousel, der eine Ref für den Viewport und eine API für scrollNext, scrollTo, selectedScrollSnap und Events zurückgibt. Das Markup besteht aus drei verschachtelten Elementen, und die Slide-Breiten legst du selbst in CSS fest. So sieht ein minimales React Carousel mit Tailwind aus:
Das lässt sich ziehen und rastet ein. Pfeile, Dots, Tastaturbedienung oder Namen für die Slides gibt es nicht, und um den aktuellen Slide anzuzeigen, abonnierst du das Event select und liest selectedScrollSnap(). Embla lässt das absichtlich weg, um klein zu bleiben, und deshalb bestehen die meisten Embla Carousel Beispiele, die du findest, vor allem aus Code für die Bedienelemente. Ein Hinweis zur Version: Die Embla-Doku startet inzwischen mit dem Release Candidate von 9.0, der diese Methoden umbenennt (goToNext, goTo, selectedSnap). Prüf also, für welche Version ein Snippet geschrieben ist, bevor du es kopierst.
Wie unterscheiden sich die Karussells von Embla, shadcn und Wingo UI?
Alle drei nutzen dieselbe Engine, Ziehen und Einrasten fühlen sich also gleich an; der Unterschied liegt darin, wie viel von der Bedienschicht mitkommt. Die Fakten stammen aus der öffentlichen Doku der Projekte, Stand Oktober 2026.
Das shadcn Carousel ist die bessere Wahl, wenn du kostenlose, minimale Teile willst und Dots und Autoplay-Steuerung ohnehin selbst schreiben willst. Reines Embla passt, wenn das Design so ungewöhnlich ist, dass dir jeder Wrapper im Weg steht. Der Rest dieses Guides behandelt die Version von Wingo UI.
Welche Props braucht ein Karussell in React?
Meist keine außer den Slides. Ohne Props zeigt das Carousel jeweils einen Slide mit Dots darunter, Pfeile bei Maus oder Trackpad und auf dem Smartphone den Anschnitt des nächsten Slides am Rand. Installier es und übergib die Slides als Children:
Jedes Child wird automatisch in einen Slide gepackt. Setz draggable={false} bei Bildern, damit das browsereigene Ziehen von Bildern nicht mit dem Wischen kollidiert. Als Nächstes brauchst du meist slidesPerView, gap (none, sm, md, lg), align (start oder center), loop, arrows (auto, always, never), arrowPosition (inside, outside, header) und indicator. Alles, was Embla unterstützt und keine eigene Prop hat, etwa slidesToScroll oder duration, geht über opts, und setApi gibt dir die Embla-API.
Zieh die Fotos, klick dann ins Karussell und nutz die Pfeiltasten links und rechts. Auf dem Smartphone schaut das nächste Inserat am Rand hervor.
$ npx wingo-ui@latest add carouselWie baue ich Inserat-Leisten und Testimonials?
Ändere slidesPerView und zwei oder drei Layout-Props. Eine Zahl zeigt so viele Slides; eine Kommazahl lässt den nächsten am Rand hervorschauen (1.15 zeigt 15% des nächsten). Ein Objekt legt den Wert je nach Breite des Karussells fest, sodass eine Leiste in einer schmalen Sidebar weniger Karten zeigt als dieselbe Leiste über die ganze Seite:
Die Breakpoints sind Container Queries auf dem Karussell selbst (sm ab 640px, md ab 768px, lg ab 1024px). Für Testimonials zentrierst du den ausgewählten Slide und lässt die anderen schrumpfen:
effect="scale" verkleinert Slides abseits des Einrastpunkts auf 0.9 und dunkelt sie ab. Statt React-State liest der Effekt eine CSS-Variable, die bei jedem Scroll-Event geschrieben wird, sodass ein Ziehen die Slides nicht neu rendert. Für eine Leiste mit Chips, die ohne Einrasten gleiten soll, nimm slidesPerView="auto" mit dragFree.
Wie verhindere ich Layout Shift beim Laden eines Karussells?
Schreib die Slide-Breiten in CSS, das der Server rendert. Embla misst die berechnete Größe deiner Slides (opens in a new tab) und setzt selbst nie Breiten. Der klassische Layout Shift entsteht also durch Code, der in einem Effect das Fenster misst und danach die Slides dimensioniert. Beim ersten Paint ist jeder Slide noch voll breit, dann springen alle auf ein Drittel.
Das Carousel macht aus slidesPerView CSS Custom Properties pro Container-Breakpoint, sodass das Server-HTML schon die endgültigen Breiten und den Anschnitt des nächsten Slides auf dem Smartphone enthält. Die Zahl der Dots wird aus der Zahl der Slides geschätzt, bevor Embla gemessen hat, also erscheint auch der Indikator nicht verspätet. Zwei Dinge bleiben bei dir:
- Gib Medien eine feste Form. Nutz eine
aspect-*-Klasse oder die Attribute width und height an jedem Bild. Ein Bild ohne Maße ändert beim Laden die Höhe des Slides, und Embla muss neu messen. - Lade die ersten Slides sofort. Markier das erste oder die ersten zwei Bilder mit
loading="eager"und den Rest mitlazy.
Wie mache ich ein Embla-Karussell barrierefrei?
Folg dem Carousel-Pattern des W3C (opens in a new tab): eine beschriftete Region mit aria-roledescription="carousel", Slides als benannte Gruppen, Buttons für Zurück und Weiter und eine Live-Region, die still bleibt, solange Autoplay läuft. Das Carousel rendert genau diese Struktur. Jeder Slide bekommt die Rolle group und einen Namen wie „3 of 8“, und der Slide-Container ist aria-live="polite", solange Autoplay pausiert, und off, während es läuft.
Die Tastaturbedienung geht über das Minimum des Patterns hinaus. Der Viewport selbst ist fokussierbar: Pfeil links und rechts blättern um einen Slide weiter (Pfeil hoch und runter bei orientation="vertical"), Pos1 und Ende springen an den Anfang und ans Ende. Eingabefelder in Slides behalten ihre eigenen Pfeiltasten. Dots und Thumbnails sind jeweils ein einziger Tab-Stopp, bei dem der Fokus der Auswahl folgt, und Slides außerhalb des Sichtbereichs sind inert, sodass Tab nie auf einem Link landet, den du nicht siehst. Wird ein Pfeil am Ende des Tracks deaktiviert, wandert der Fokus zum anderen Pfeil, statt auf der Seite verloren zu gehen.
Benenn das Karussell mit label nach seinem Inhalt. Der Standard ist „Carousel“, was ein Screenreader als „Carousel, carousel“ vorliest, und das W3C-Pattern rät vom Wort Carousel im Label ab. Jeder Text, auch die Namen „3 of 8“ und „Go to slide 3“, steckt in labels und wird mit DEFAULT_CAROUSEL_LABELS zusammengeführt. Zum Übersetzen musst du also nichts forken.
Wie baue ich Autoplay, das man stoppen kann?
Setz autoplay und sorg für einen Pause-Button: Das Carousel rendert ihn immer, solange Autoplay an ist. Das WCAG-2.2-Erfolgskriterium 2.2.2 Pause, Stop, Hide (opens in a new tab) (Stufe A) verlangt einen Weg, Bewegung zu pausieren, die von selbst startet und länger als fünf Sekunden dauert. Das offizielle Plugin für Embla Carousel Autoplay stoppt standardmäßig nach einem Ziehen (stopOnInteraction: true) und stellt play() und stop() bereit; das sichtbare Bedienelement baust du selbst.
Den Timer bringt die Komponente selbst mit, ein Plugin braucht es nicht. Er pausiert, solange eine Maus über dem Karussell liegt (pauseOnHover), solange der Tastaturfokus darin ist, während des Ziehens, wenn der Tab im Hintergrund ist und wenn das Karussell aus dem sichtbaren Bereich gescrollt ist. Ohne loop stoppt er auf dem letzten Slide. stopOnInteraction entscheidet, ob ein Ziehen oder ein Pfeil den Timer neu startet (der Standard) oder Autoplay anhält, bis jemand auf Play drückt. Ist reduzierte Bewegung eingestellt, startet Autoplay pausiert, und Pfeile springen, statt zu gleiten; der Guide zu prefers-reduced-motion erklärt, wie das ohne Hydration Mismatch gelesen wird.
Schalt unten Autoplay ein und sieh zu, wie sich die Story-Balken füllen. Fahr dann mit der Maus über das Karussell oder spring mit Tab hinein, um es zu pausieren.
$ npx wingo-ui@latest add carouselWie verhält sich das Karussell auf dem Smartphone?
Es lässt sich wischen und kommt dem vertikalen Scrollen nicht in die Quere. Unter 640px Karussellbreite zeigt ein ganzzahliges slidesPerView 12% des nächsten Slides, damit man sieht, dass noch mehr kommt (peek={false} schaltet das ab). Mit arrows="auto" verschwinden die Pfeile auf Touch-Geräten, Dots bekommen 44px große Tippflächen, und der Track setzt touch-action: pan-y, damit ein vertikales Wischen weiterhin die Seite scrollt. Auf dem Laptop bewegt ein seitliches Wischen mit zwei Fingern auf dem Trackpad genau einen Slide pro Wischen und löst nicht die Zurück-Geste des Browsers aus; setz wheel={false}, wenn das Karussell in einem Container steckt, der seitlich scrollt.
Wie baue ich Onboarding-Slides mit Embla?
Nimm das Onboarding Carousel, das auf dem Carousel aufbaut. Es ergänzt Skip, ein Next, das auf dem letzten Slide zu Get started wird, eine Hintergrundfarbe pro Slide, die beim Wischen überblendet, und persistKey, das eine abgeschlossene Tour im localStorage speichert, damit sie nicht noch einmal erscheint.
layout="screen" füllt den Viewport mit Safe-Area-Abstand, card erzeugt eine Fläche von 420 mal 640px, und die Prop modal öffnet es in einem Dialog, der auf dem Smartphone den ganzen Bildschirm füllt und ab 768px eine zentrierte Karte ist. Jeder Slide wird angesagt, sobald er eingerastet ist, und Escape im Modal zählt als Skip.
$ npx wingo-ui@latest add onboarding-carouselFür einen eigenen Footer in einem einfachen Karussell nimmst du die zusammensetzbaren Teile (CarouselRoot, CarouselViewport, CarouselSlide, CarouselDots) und den Hook useCarousel, die dir selectedIndex, snapCount, scrollNext und scrollTo liefern. Damit ist ein Continue-Button in ein paar Zeilen gebaut. Das Tutorial zum React-Onboarding-Flow kombiniert diese Slides mit Berechtigungsabfragen und einem Install-Prompt.
Was sind die typischen Fallstricke bei Embla-Karussells?
- Bilder, die sich ziehen lassen. Ohne
draggable={false}packt ein Ziehen mit der Maus auf einem Foto das Bild selbst, statt den Track zu bewegen. - Links in verborgenen Slides. Tab läuft in Slides außerhalb des Sichtbereichs, solange sie nicht inert sind, und wer die Tastatur nutzt, landet auf einem Link, den er nicht sieht.
- Autoplay ohne Pause-Button. Bewegung, die ohne einen solchen Button länger als fünf Sekunden läuft, verstößt gegen WCAG 2.2.2, und sie schiebt den Slide weg, während jemand ihn erst halb gelesen hat.
- Ein Karussell für Inhalte, die jeder sehen muss. Alles auf Slide vier bleibt verborgen, bis jemand dreimal wischt. Pack das, worauf es ankommt, in eine statische Karte und nutz Karussells zum Stöbern, wie in der Marketplace-Vorlage.
Wie füge ich es meinem Projekt hinzu?
Führ npx wingo-ui@latest add carousel aus (oder onboarding-carousel). Die CLI kopiert den Quellcode nach components/ui, ergänzt den Button sowie die gemeinsamen Hooks und Helfer, die die Komponente importiert, und installiert embla-carousel-react, motion und lucide-react. Beide Karussells sind Pro-Komponenten (Preise). Weitere Deep Dives zu einzelnen Komponenten findest du in der Kategorie Komponenten-Guides, und der Guide zu React-Formularkomponenten wendet dieselben Regeln für Tastatur, Touch und Screenreader auf jedes Formularelement an.
Häufige Fragen
Was ist Embla Carousel in React?
Embla Carousel ist eine Karussell-Engine unter MIT-Lizenz ohne Abhängigkeiten. Das Paket embla-carousel-react stellt den Hook useEmblaCarousel bereit, der eine Ref für den Viewport und eine API für Scrollen, Events und Zustand zurückgibt; Markup, CSS und Bedienelemente schreibst du selbst.
Wie füge ich einem Embla-Karussell Autoplay hinzu?
Mit reinem Embla installierst du das offizielle Plugin embla-carousel-autoplay und übergibst es an useEmblaCarousel; in Version 8 liegt die dokumentierte Standardverzögerung bei 4000 ms, und es stoppt, sobald jemand zieht (stopOnInteraction ist standardmäßig true). Einen Pause-Button musst du trotzdem selbst rendern, denn WCAG 2.2.2 verlangt einen Weg, Bewegung zu stoppen, die länger als fünf Sekunden läuft.
Basiert das shadcn Carousel auf Embla?
Ja. Das Carousel von shadcn/ui legt Carousel, CarouselContent, CarouselItem, CarouselPrevious und CarouselNext um Embla, setzt Slide-Breiten über basis-Klassen und nimmt Embla-Optionen und Plugins als Props. Dots, Thumbnails oder einen Pause-Button für Autoplay liefert die Doku nicht mit, die baust du aus der API.
Wie verhindere ich, dass ein Embla-Karussell beim Laden das Layout verschiebt?
Leg die Slide-Breiten in CSS fest, damit das Server-HTML schon die richtige Größe hat, und gib jedem Bild ein festes Seitenverhältnis. Embla misst die Slides nach dem Mount und setzt selbst nie Breiten, also springt alles, was nach der Hydration per JavaScript dimensioniert wird.
Ist das Carousel von Wingo UI kostenlos?
Nein. Carousel und Onboarding Carousel sind Pro-Komponenten. Button und Card aus den Beispielen sind kostenlos.
- Karussell
- Embla
- Barrierefreiheit
- Mobile UI
- React


