Extended Floating Action Button (Extended FAB)
Extended floating action buttons help people take primary actions on wide layouts or long scrollable views, using both an icon and text label for clarity.
Extended floating action buttons (Extended FABs) are more prominent than regular FABs. They feature both a text label and an icon to clarify the action. They can transform/collapse into standard FABs (icon-only) on scroll.
Introduction
Material Design 3 Expressive Extended FABs are perfect for screens with long, scrolling views that require persistent access to a primary action (e.g., checkout, publish post). They feature high-fidelity spring motion, shape-morphing physics on tap, and native support for internationalization (RTL). Unlike baseline Extended FABs, the modern Expressive spec replaces the deprecated baseline with the Small (56dp) variant utilizing Title Medium typography.
Anatomy
- Container: A rounded rectangular surface that wraps the inner layout. Hugs content dynamically by default.
- Icon (Optional): Intuitively represents the action. Replaced automatically with a spinner when loading.
- Label Text (Required): Describes the action clearly. Recommended to be 1–2 words at most. Avoids wrapping or truncation.
- State Layer: Shows interactive feedback overlay (hover, focus, pressed) using MD3 expressive ripples.
Variants
Sizes
The M3 Expressive specification defines three variants of Extended FABs:
- Small (56dp): Height 56dp. Replaces the deprecated baseline layout. Title Medium font size.
- Medium (80dp): Height 80dp. Title Large font size.
- Large (96dp): Height 96dp. Maximum prominence on spatial UIs or compact screen layouts. Headline Small font size.
Color Styles & Customizations
Extended FABs can map to three primary color container tokens: Primary, Secondary, and Tertiary. You can also utilize the lowered prop to reduce resting elevation when nesting inside app bars, or omit the icon completely for label-only FABs.
Features
Scroll Collapse & Controlled Mode
The Extended FAB can collapse into an icon-only pill-shaped FAB when scrolling down to temporarily occupy less screen space, and expand again when scrolling up. You can toggle this auto behavior via collapseOnScroll or manually control the state from outside via the collapsed prop.
Right-to-Left (RTL) Support
In right-to-left layout orientations (e.g. Arabic, Hebrew), Extended FABs automatically mirror their layout, positioning the icon to the right of the label text.
Container Transform (Morphing)
Using the newly added layoutId prop along with Framer Motion's Layout Projection, the Extended FAB can morph seamlessly into another surface (like a compose dialog, card, or panel).
Usage
Basic Usage
import { ExtendedFAB, Icon } from "@bug-on/m3-expressive";
<ExtendedFAB
label="Compose"
icon={<Icon name="edit" />}
onClick={() => console.log("Click!")}
/>
Auto Collapse on Scroll
import { ExtendedFAB, Icon } from "@bug-on/m3-expressive";
// Automatically collapses when scroll down passes 50px threshold
<ExtendedFAB
label="Compose"
icon={<Icon name="edit" />}
collapseOnScroll
scrollThreshold={50}
/>
Best Practices
Do
- Use only one Extended FAB per screen to maintain a clean visual hierarchy.
- Keep the label text to 1-2 words at most to prevent overcrowding.
- Place the Extended FAB above content panels or in consistent navigation rails.
Next.js Integration (asChild)
Use asChild to wrap Next.js Link with Extended FAB styling and collapse behavior.
import Link from "next/link";
import { ExtendedFAB, Icon } from "@bug-on/m3-expressive";
<ExtendedFAB asChild label="Compose" icon={<Icon name="edit" />}>
<Link href="/compose">Compose</Link>
</ExtendedFAB>
Don't
- Don't use multiple Extended FABs on a single screen.
- Don't use baseline configurations (
surfacecolor, large-baseline button size) which are deprecated. - Don't let the text label wrap into multiple lines.
Design Tokens
Measurements & Layout
| Size | Container Height | Shape Radius (default) | Shape Radius (collapsed) | Icon Size | Typography |
|---|---|---|---|---|---|
| Small | 56dp (h-14) | 16px | 28px (pill) | 24dp | Title Medium |
| Medium | 80dp (h-20) | 20px | 40px (pill) | 28dp | Title Large |
| Large | 96dp (h-24) | 28px | 48px (pill) | 36dp | Headline Small |
Color Mapping
| Token Role | Tailwind Bg | Tailwind Text |
|---|---|---|
| Primary | bg-m3-primary-container | text-m3-on-primary-container |
| Secondary | bg-m3-secondary-container | text-m3-on-secondary-container |
| Tertiary | bg-m3-tertiary-container | text-m3-on-tertiary-container |
Accessibility
- ARIA Accessible Name: The
aria-labelautomatically defaults to the requiredlabelprop. When collapsed, thearia-labelremains present for screen readers. - Interactive States: Keyboard navigation is fully supported out of the box (
Tabfocus, interaction viaEnterorSpacekeys). - Reduced Motion: Entrance, exit, and collapse width transitions respect standard systems'
prefers-reduced-motionconfigurations.
API Reference
ExtendedFAB
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | — | Required. The text label to display. |
icon | ReactNode | — | The icon to display next to the label (optional). |
size | "sm" | "md" | "lg" | "sm" | The size of the Extended FAB according to the Expressive spec. |
colorStyle | "primary" | "secondary" | "tertiary" | "primary" | The background color container role. |
lowered | boolean | false | Reduces the resting elevation (shadow) of the FAB to shadow-sm. |
loading | boolean | false | Displays a loading state and disables interaction. |
loadingVariant | "loading-indicator" | "circular" | "loading-indicator" | The type of loading spinner to display. |
visible | boolean | true | Controls the visibility of the FAB with a scale transition. |
collapseOnScroll | boolean | false | Automatically collapses into an icon-only FAB when scrolling down. |
scrollThreshold | number | 50 | The minimum scroll threshold (in px) to trigger collapse. |
collapsed | boolean | — | Manually controls the collapsed state from the outside. |
onCollapsedChange | (collapsed: boolean) => void | — | Callback triggered when the collapsed state changes. |
asChild | boolean | false | Renders as the child element while keeping styles. |
layoutId | string | — | Unique identifier for Container Transform (seamless surface morphing). |