React Floating Action Button: Speed Dial und Safe Areas

Ein React Floating Action Button wirkt wie die einfachste Komponente auf einem Smartphone-Screen: ein runder Button, position: fixed, bottom: 16px, right: 16px. Dann wächst das Produkt. Aus einer Aktion zum Anlegen werden vier, das Label verdeckt die Liste, der Button landet auf der Tab-Leiste, auf der Produktseite kommt eine Kaufleiste dazu, und unten sitzt der Home-Indikator des iPhones. Dieser Guide löst jeden dieser Punkte mit dem FAB von Wingo UI: wann ein FAB das richtige Bedienelement ist, das vertikale und das radiale Speed Dial, das Label, das beim Scrollen einklappt, das Stapeln über der Bottom Tab Bar und der Sticky CTA Bar und die Details zur Barrierefreiheit, die die meisten Beispiele weglassen.
Wann braucht ein Screen einen Floating Action Button?
Wenn es auf dem Screen eine Aktion gibt, die Leute viel öfter ausführen als jede andere, und diese Aktion etwas Neues anlegt: einen Kunden, eine Nachricht, ein Inserat. Die FAB-Doku von Material Components (opens in a new tab) beschreibt ihn als den Button für die wichtigste oder häufigste Aktion eines Screens, und die FAB-Doku von MUI (opens in a new tab) ergänzt, dass pro Screen nur einer empfohlen wird. Eine Einstellungsseite, ein Checkout oder ein Formular mit eigenem Absenden-Button braucht keinen.
Genau darum sitzt er unten in der Ecke. In Steven Hoobers Feldstudie von 2013 (opens in a new tab) (1.333 Beobachtungen von Menschen an mobilen Geräten) hielten 49 % der Leute, die den Screen berührten, das Smartphone in einer Hand. Ein Button zum Anlegen in der oberen Leiste ist weit weg von diesem Daumen, ein FAB unten in der Ecke liegt genau darunter. Auf dem Desktop gehört dieselbe Aktion meist in den Seitenkopf. Rendere den FAB also nur unter 768px (className="md:hidden"), wenn der Kopf den Button schon hat.
Wie installiere und rendere ich einen einfachen FAB?
Installier ihn mit der CLI. Sie kopiert components/ui/fab.tsx in dein Projekt, zusammen mit den Registry-Abhängigkeiten: dem Tooltip, vier Hooks (use-scroll-state, use-keyboard-inset, use-shortcut, use-controllable-state) und den Helfern haptics, motion, navigation, tones und ui-config. Auf der npm-Seite kommen motion und lucide-react dazu.
Ohne actions ist der FAB ein normaler Button, und onClick ist die Aktion. Gib ihm ein label, dann startet er ausgeklappt, als Pille mit Icon und Text:
Die Defaults: size="md" ist ein Button mit 56px und einem Icon mit 24px (dieselben 56dp wie beim normalen FAB von Material), tone="neutral" ist im Light Mode schwarz und im Dark Mode weiß, position="fixed" heftet ihn an den Viewport, und side="end" setzt ihn auf Seiten mit Leserichtung links nach rechts an den rechten Rand und auf Seiten mit Leserichtung rechts nach links an den linken. Nimm tone="brand" nur, wenn genau diese Aktion der Grund ist, warum es den Screen gibt, etwa „Inserat aufgeben“ auf einem Marktplatz.
Wie funktioniert ein React FAB Speed Dial?
Übergib 2 bis 6 actions, und der Button öffnet ein Dial, statt eine Aktion auszuführen. Jede Aktion ist { id, label, icon, onSelect?, href?, tone?, disabled? }. Eine Aktion mit href rendert einen Link über linkComponent (oder den LinkProvider der App) und schließt das Dial:
Sortier das Array nach Häufigkeit: Die erste Aktion erscheint dem Daumen am nächsten. Öffnet sich das Dial, wird die Seite dahinter von einem Scrim abgedunkelt (ein Tipp darauf schließt es), das Plus dreht sich um 45 Grad zu einem x, und auf Touch-Geräten vibriert das Smartphone einmal kurz über den Haptics-Helfer. Das Label klappt ein, sobald sich das Dial öffnet, damit der Auslöser wieder der runde Button ist. Das passt zur Doku zum FAB-Menü von Material (opens in a new tab): Dort steht, dass das Menü nicht mit ausgeklappten FABs verwendet wird und das Speed Dial sowie gestapelte kleine FABs ersetzen soll.
$ npx wingo-ui@latest add fabHalte das Dial kurz. Die Material-Doku setzt das Menü auf 2 bis 6 verwandte Aktionen, die Speed-Dial-Doku von MUI (opens in a new tab) auf drei bis sechs und rät, ab mehr als sechs etwas anderes als einen FAB zu nehmen (beides geprüft im Oktober 2026). Der FAB rendert jede Aktion, die du übergibst, gibt aber ab mehr als sechs eine Warnung im Development-Modus aus. Ein siebter Eintrag fällt also in der Konsole auf, bevor er im Review auffällt.
Vertikal oder radial?
layout="vertical" ist das Muster von Material: eine Spalte runder Aktionen über dem Button, jede mit einem Label-Chip daneben, und die ganze Zeile ist die Tippfläche. Das ist der Default, weil die Labels immer sichtbar sind. Auf Touch-Geräten zählt das, denn dort gibt es kein Hover.
layout="radial" lässt kleine runde Aktionen auf einem Viertelbogen ausfahren (einem Halbbogen mit side="center"). Das verdeckt weniger von der Seite und passt zu einer Karte oder einem Foto, aber die Labels wandern in Tooltips, die bei Hover, Tastaturfokus oder langem Drücken erscheinen. Nimm es für drei oder vier Aktionen mit eindeutigen Icons, etwa Kamera, Galerie und Standort.
$ npx wingo-ui@latest add fabWie lasse ich einen Extended FAB beim Scrollen einklappen?
Setz ein label und lass extended="auto" auf dem Default. Das Label ist oben auf der Seite und beim Hochscrollen zu sehen und klappt beim Runterscrollen zum Icon ein, sobald die Seite mehr als 8px vom oberen Rand entfernt ist. Die Breitenänderung ist eine Layout-Animation mit Spring, gesteuert von der Scrollrichtung, die useScrollState meldet. extended="always" behält die Pille, extended="never" zeigt nur das Icon und verschiebt das Label in einen Tooltip.
Einen Extended FAB beim Scrollen einklappen zu lassen ist die sanfte Variante. hideOnScroll ist die starke: Der ganze Button gleitet beim Runterscrollen hinaus, folgt dabei dem Finger und kommt beim Hochscrollen zurück. Das Gleiten läuft über Motion Values, also gibt es keinen React-Render pro Frame. Er verschwindet nie, solange sein Dial offen ist oder er den Tastaturfokus hat, und inert sorgt dafür, dass Tab nicht auf ihm landet, solange er weg ist. Wir lassen es standardmäßig aus, weil man einen Button, den man nicht sieht, auch nicht antippen kann. Schalte es für Feeds und Posteingänge ein, in denen der FAB Text verdeckt, den Leute gerade lesen.
Der FAB geht außerdem aus dem Weg, solange die Bildschirmtastatur offen ist (hideOnKeyboard, standardmäßig an), und schließt ein offenes Dial. So verdeckt er nie das Feld, in das du tippst. Scrollt die Seite in einem Element statt im Fenster, beobachtet der FAB den falschen Scroller und klappt nie ein. Pack den Inhalt in <ScrollContainerProvider value={scrollerElement}> aus @/hooks/use-scroll-state oder übergib das Element oder eine Ref als scrollTarget. Mit position="sticky" oder "absolute" und ohne Provider und scrollTarget sucht er sich den nächsten scrollenden Parent selbst.
Wie staple ich einen FAB über Tab-Leiste, Kaufleiste und Home-Indikator?
Jede Leiste veröffentlicht ihre Höhe als CSS-Variable, und der FAB addiert sie. Eine handgeschriebene Floating Action Button Tailwind-Klasse scheitert genau hier: bottom-4 landet auf dem Smartphone unter der Tab-Leiste, und bottom-[calc(64px+env(safe-area-inset-bottom)+16px)] sitzt zu hoch, sobald die Leiste unten selbst dasselbe Inset als Padding bekommt, weil das Inset dann doppelt zählt.
In Wingo UI teilen sich die drei Komponenten zwei Variablen. Die Bottom Tab Bar schreibt --bottom-bar-offset (ihre Höhe inklusive der Safe Area, die sie abdeckt) auf <html>, wenn sie fixiert ist. Die Sticky CTA Bar schreibt --cta-bar-offset, ihre sichtbare Höhe, die auf 0 fällt, solange sie versteckt ist. Der FAB liest beide und berechnet seine Unterkante so:
Ist eine Leiste zu sehen, enthält sie das Inset schon, und der FAB addiert nur seinen offset von 16px. Ohne Leiste gewinnt das Inset allein, und der FAB hält Abstand zum Home-Indikator. Eine Produktseite mit allen dreien braucht keine zusätzliche Rechnung:
Die Kaufleiste sitzt auf der Tab-Leiste (ihre eigene Unterkante ist var(--bottom-bar-offset)), und der FAB schwebt 16px über der Kaufleiste. Versteckt sich die Kaufleiste beim Scrollen (hideOnScroll ist standardmäßig aus, das Snippet schaltet es also ein), fällt --cta-bar-offset Frame für Frame auf 0, und der FAB folgt nach unten. In einem Scroll-Container (einem Smartphone-Rahmen, einem Panel) nimm position="sticky" für alle drei und rendere sie am Ende des Scrollers, nach dem Inhalt. Die Safe Area erklärt der Beitrag zu Tailwind Safe Area Insets ausführlich, die Tab-Leiste selbst der Guide zur React Bottom Navigation Bar.
Was braucht ein FAB für Tastatur- und Screenreader-Nutzer?
Er braucht einen Fokusstopp pro Aktion, Menü-Semantik mit einem Expanded-Zustand am Auslöser, einen Namen, der ohne sichtbares Label funktioniert, und Navigation mit den Pfeiltasten. Ein Beitrag im Joplin-Forum vom Juni 2022 (opens in a new tab) über die mobile App von Joplin listet die typischen Fehler eines Speed Dial mit TalkBack auf: Button und Label jeder Aktion waren beide fokussierbar, das Öffnen des Menüs wurde nicht angesagt, und der Umschalter hieß offen wie geschlossen gleich. Der FAB von Wingo UI löst jeden dieser Punkte:
- Ein Element pro Aktion. Jede Aktion ist ein einzelner
menuitem-Button oder -Link, der Label-Chip und Icon enthält, also hält ein Screenreader pro Aktion nur einmal an. - Zustand am Auslöser. Der Button hat
aria-haspopup="menu",aria-expandedundaria-controls; das Dial ist einrole="menu"mit einem Namen auslabels.menu. - Ein Name für den reinen Icon-Button. Mit
labelist das der Name; ohne ist eslabels.open/labels.closefür ein Dial undlabels.actionfür einen normalen Button, alle für Übersetzungen überschreibbar. - Tastatur. Enter, Leertaste oder Pfeil nach oben öffnen das Dial und fokussieren die nächstgelegene Aktion; die Pfeiltasten bewegen den Fokus, Pos1 und Ende springen zur ersten und zur letzten Aktion, Escape schließt und gibt den Fokus an den Button zurück, und Tab schließt und geht weiter.
shortcut="n"öffnet es von überall und wird alsaria-keyshortcutsgesetzt. - Tippflächen. Der Auslöser hat je nach Größe 48, 56 oder 64px, die Aktionen im Dial 40, 44 oder 48px; die Aktion
smmit 40px wächst bei groben Zeigern auf 44px, und eine vertikale Zeile ist samt Chip mindestens 44px hoch, das Minimum, das wir überall verwenden (siehe Größen für Tippflächen).
Ist reduzierte Bewegung eingestellt, werden das Dial und das Hinausgleiten beim Scrollen zu einfachen Überblendungen.
Was sind die häufigsten Fehler beim Floating Action Button?
Die meisten haben damit zu tun, wo der Button auf dem Screen sitzt:
- Zwei FABs auf einem Screen. Brauchst du einen zweiten schwebenden Button, gehört einer davon in den Kopf oder ins Dial.
- Ein FAB über der letzten Zeile. Gib dem Scrollbereich unten so viel Padding, wie der FAB hoch ist, plus seinen Abstand (die Demo nutzt
pb-28), sonst landet der letzte Listeneintrag unter dem Button. - Ein FAB als Navigation. „Zum Posteingang“ ist ein Tab. Der FAB legt etwas auf dem aktuellen Screen an oder startet es.
- Ein fixierter FAB in einem transformierten Parent.
position: fixedbezieht sich auf den nächsten Vorfahren mittransform,filteroderwill-change: transform, also zieht ein Wrapper für Seitenübergänge den FAB mit der Seite mit. Rendere ihn außerhalb dieses Wrappers oder nimm bewusstposition="absolute"in einem relativen Container. - Ein Material FAB Web-Nachbau, der die Safe Area ignoriert. Eine Webseite muss sie mit
viewport-fit=covererst aktivieren undenv(safe-area-inset-bottom)selbst lesen, sonst sitzt der Button auf dem Home-Indikator. Teste auf einem echten iPhone, nicht nur in einem verkleinerten Desktop-Fenster.
Was baue ich als Nächstes?
Installier den FAB mit npx wingo-ui@latest add fab und probier die Props im FAB-Playground aus. Er gehört zu Wingo UI Pro, zusammen mit der Tab-Leiste und der Kaufleiste, mit denen er sich stapelt. Für den Rest des unteren Bildschirmrands auf dem Smartphone erklärt der Guide zu Mobile-first React-Komponenten die neun Regeln, denen diese Komponenten folgen. Weitere Beiträge findest du unter Mobile UI in React.
Häufige Fragen
Wie viele Aktionen sollte ein FAB Speed Dial haben?
Zwei bis sechs. Laut der Doku zum FAB-Menü von Material Components zeigt es 2 bis 6 verwandte Aktionen, und die Speed-Dial-Doku von MUI nennt drei bis sechs und rät, ab mehr als sechs etwas anderes als einen FAB zu nehmen. Bei mehr Aktionen nimm ein Action Sheet oder ein Menü.
Soll sich ein Floating Action Button beim Scrollen verstecken?
Nur in langen Listen, in denen er Inhalte verdeckt, die Leute lesen. Wenn er sich versteckt, sollte er dem Finger folgen, beim Hochscrollen sofort zurückkommen und nie verschwinden, solange sein Dial offen ist oder er den Tastaturfokus hat. Oft reicht es schon, das Label beim Scrollen einzuklappen, und die Aktion bleibt erreichbar.
Hat shadcn/ui einen Floating Action Button?
Stand Oktober 2026 nicht: Die Komponentenliste enthält Button, Button Group und Dropdown Menu, aber keinen FAB und kein Speed Dial. Der FAB von Wingo UI wird genauso installiert, als Quellcode in deinem Projekt, mit npx wingo-ui@latest add fab.
Wie halte ich einen FAB über einer Bottom Navigation Bar?
Miss die Leiste und addier ihre Höhe zum unteren Abstand des FAB. Die Bottom Tab Bar von Wingo UI schreibt --bottom-bar-offset, die Sticky CTA Bar schreibt --cta-bar-offset, und der FAB addiert beide und nimmt dann den größeren Wert aus dieser Summe und dem Safe-Area-Inset. So zählt der Home-Indikator nur einmal.
Ist der FAB von Wingo UI kostenlos?
Nein. Der FAB ist eine Pro-Komponente, genau wie die Bottom Tab Bar und die Sticky CTA Bar, mit denen er sich stapelt. Der Tooltip, den er für die Namen reiner Icon-Buttons nutzt, ist kostenlos.
- Mobile UI
- FAB
- Speed Dial
- React
- Tailwind CSS


