# React Floating Action Button: Speed Dial und Safe Areas

> Ein React Floating Action Button für den Daumen: Speed Dial mit 2 bis 6 Aktionen, einklappendes Label und Platz über Tab-Leiste und Home-Indikator.

- Autor: [Serban Rusu](https://wingo-ui.com/blog/authors/serban), Founder of Wingo UI
- Veröffentlicht: 16. Okt. 2026
- Kategorie: [Mobile UI mit React](https://wingo-ui.com/de/blog/category/mobile-ui)
- Lesezeit: 9 Min.
- Kanonische URL: https://wingo-ui.com/de/blog/react-floating-action-button
- Englische Fassung: https://wingo-ui.com/blog/react-floating-action-button

## Kurz gesagt

Ein React Floating Action Button ist ein einzelner runder Button unten in der Ecke für die Hauptaktion des Screens, der auf Wunsch ein Speed Dial mit 2 bis 6 verwandten Aktionen als Menü öffnet. Im Web muss er außerdem sein Label beim Scrollen einklappen, über einer Bottom Tab Bar, einer festen Kaufleiste und dem Home-Indikator des iPhones sitzen, ohne die Safe Area doppelt zu zählen, und mit Tastatur und Screenreader funktionieren. Der FAB von Wingo UI deckt das über Props ab: actions, layout="vertical" oder "radial", extended="auto" und hideOnScroll.

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](https://wingo-ui.com/components/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](https://wingo-ui.com/components/bottom-tab-bar) und der [Sticky CTA Bar](https://wingo-ui.com/components/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](https://github.com/material-components/material-components-android/blob/master/docs/components/FloatingActionButton.md) beschreibt ihn als den Button für die wichtigste oder häufigste Aktion eines Screens, und die [FAB-Doku von MUI](https://mui.com/material-ui/react-floating-action-button/) 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](https://www.uxmatters.com/mt/archives/2013/02/how-do-users-really-hold-mobile-devices.php) (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.

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

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:

```tsx
"use client";

import { Pencil } from "lucide-react";
import { Fab } from "@/components/ui/fab";

export function ComposeFab({ onCompose }: { onCompose: () => void }) {
  return <Fab icon={<Pencil />} label="Verfassen" onClick={onCompose} className="md:hidden" />;
}
```

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:

```tsx
"use client";

import Link from "next/link";
import { FileText, Handshake, StickyNote, UserPlus } from "lucide-react";
import { Fab, type FabAction } from "@/components/ui/fab";

export function ClientsFab({ onCreate }: { onCreate: (kind: string) => void }) {
  const actions: FabAction[] = [
    { id: "client", label: "Neuer Kunde", icon: <UserPlus />, onSelect: () => onCreate("client") },
    { id: "invoice", label: "Neue Rechnung", icon: <FileText />, onSelect: () => onCreate("invoice") },
    { id: "quote", label: "Neues Angebot", icon: <Handshake />, href: "/quotes/new" },
    { id: "note", label: "Notiz", icon: <StickyNote />, onSelect: () => onCreate("note") },
  ];
  return <Fab label="Hinzufügen" actions={actions} shortcut="n" linkComponent={Link} />;
}
```

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](https://wingo-ui.com/de/blog/web-vibration-api-haptic-feedback). 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](https://github.com/material-components/material-components-android/blob/master/docs/components/FloatingActionButtonMenu.md): Dort steht, dass das Menü nicht mit ausgeklappten FABs verwendet wird und das Speed Dial sowie gestapelte kleine FABs ersetzen soll.

> Live-Demo (FAB): Tippe auf Add, um das Speed Dial zu öffnen, wähl New invoice und sieh zu, wie sich der Kopf aktualisiert, dann scroll die Kundenliste: Das Label klappt beim Runterscrollen ein und kommt beim Hochscrollen zurück. Probier sie unter [FAB](https://wingo-ui.com/components/fab) aus und installier sie mit `npx wingo-ui@latest add fab`.

Halte das Dial kurz. Die Material-Doku setzt das Menü auf 2 bis 6 verwandte Aktionen, die [Speed-Dial-Doku von MUI](https://mui.com/material-ui/react-speed-dial/) 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.

> Live-Demo (FAB): Dasselbe Dial mit layout="radial": Tippe auf den Button, und die vier Aktionen fächern sich auf einem Bogen auf; fahr mit der Maus darüber oder drück lange, um ihren Namen zu sehen. Probier sie unter [FAB](https://wingo-ui.com/components/fab) aus und installier sie mit `npx wingo-ui@latest add fab`.

## Wie 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:

```css
bottom: calc(
  max(var(--bottom-bar-offset, 0px) + var(--cta-bar-offset, 0px), env(safe-area-inset-bottom, 0px))
  + 16px
);
```

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:

```tsx
"use client";

import Link from "next/link";
import { House, MessageCircle, Search, UserRound } from "lucide-react";
import { BottomTabBar, type BottomTabBarItem } from "@/components/ui/bottom-tab-bar";
import { Fab } from "@/components/ui/fab";
import { StickyCtaBar } from "@/components/ui/sticky-cta-bar";

const tabs: BottomTabBarItem[] = [
  { label: "Start", href: "/", icon: <House /> },
  { label: "Suche", href: "/search", icon: <Search /> },
  { label: "Profil", href: "/profile", icon: <UserRound /> },
];

export function ListingChrome({ onBuy, onMessage }: { onBuy: () => Promise<void>; onMessage: () => void }) {
  return (
    <>
      <Fab icon={<MessageCircle />} labels={{ action: "Verkäufer anschreiben" }} onClick={onMessage} />
      <StickyCtaBar price={349} locale="en-US" actionLabel="Jetzt kaufen" tone="brand" onAction={onBuy} hideOnScroll />
      <BottomTabBar items={tabs} linkComponent={Link} className="md:hidden" />
    </>
  );
}
```

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](https://wingo-ui.com/de/blog/tailwind-safe-area-insets) ausführlich, die Tab-Leiste selbst der [Guide zur React Bottom Navigation Bar](https://wingo-ui.com/de/blog/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](https://discourse.joplinapp.org/t/floating-action-button-accessibility/26197) ü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-expanded` und `aria-controls`; das Dial ist ein `role="menu"` mit einem Namen aus `labels.menu`.
- Ein Name für den reinen Icon-Button. Mit `label` ist das der Name; ohne ist es `labels.open` / `labels.close` für ein Dial und `labels.action` fü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 als `aria-keyshortcuts` gesetzt.
- Tippflächen. Der Auslöser hat je nach Größe 48, 56 oder 64px, die Aktionen im Dial 40, 44 oder 48px; die Aktion `sm` mit 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](https://wingo-ui.com/de/blog/minimum-touch-target-size-react-tailwind)).

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: fixed` bezieht sich auf den nächsten Vorfahren mit `transform`, `filter` oder `will-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 bewusst `position="absolute"` in einem relativen Container.
- Ein Material FAB Web-Nachbau, der die Safe Area ignoriert. Eine Webseite muss sie mit `viewport-fit=cover` erst aktivieren und `env(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](https://wingo-ui.com/components/fab) aus. Er gehört zu [Wingo UI Pro](https://wingo-ui.com/de/pricing), 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](https://wingo-ui.com/de/blog/mobile-first-react-components) die neun Regeln, denen diese Komponenten folgen. Weitere Beiträge findest du unter [Mobile UI in React](https://wingo-ui.com/de/blog/category/mobile-ui).

## Komponenten in diesem Artikel

- [FAB](https://wingo-ui.com/components/fab) (Pro): A React floating action button (FAB) for a phone screen's main action, with a labeled or radial speed dial and a label that folds on scroll. Installation: `npx wingo-ui@latest add fab`
- [Bottom Tab Bar](https://wingo-ui.com/components/bottom-tab-bar) (Pro): A React bottom tab bar for phones: 3 to 5 tabs at the thumb with an animated pill, badges, a raised center action and hide on scroll. Installation: `npx wingo-ui@latest add bottom-tab-bar`
- [Sticky CTA Bar](https://wingo-ui.com/components/sticky-cta-bar) (Pro): A React sticky CTA bar for product, listing and checkout pages on phones: the price on one side, the main action on the other. Installation: `npx wingo-ui@latest add sticky-cta-bar`

## 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.

---

Quelle: https://wingo-ui.com/de/blog/react-floating-action-button
