# Fab & SpeedDial (React Native)

> The floating action button for a screen's main action, extended with a label, and a SpeedDial of related actions.

GramproKit for React Native · Mobile · Beta · Source: https://kit.gramproindia.com/native/docs/fab

> **Beta:** React Native support is in beta. APIs may change or break between prereleases (npm tag `native`).

_Interactive demo:_ [open the live example](https://kit.gramproindia.com/native/docs/fab). The example's source:

```tsx
import { useState } from "react";
import { ScrollView, Text, View } from "react-native";
import { Fab, SpeedDial } from "@/component-lib/fab";
import { BottomNavigation } from "@/component-lib/bottom-navigation";
import { useKitTheme } from "@/component-lib/shared/native";
import { Row, Section } from "./layout"; // Section and Row: page layout for the demo
import { FakeRows, MiniScreen, TextIcon } from "./mobileFrame";

export function FabDemo() {
  const { tokens } = useKitTheme();
  const [extended, setExtended] = useState(true);
  const [last, setLast] = useState("—");
  const plus = (color: string, size = 22) => <TextIcon glyph="+" color={color} size={size} />;
  return (
    <>
      <Section title="Fab · sizes and variants">
        <Row>
          <Fab placement="none" size="sm" icon={plus(tokens.accentFg, 18)} aria-label="Add" />
          <Fab placement="none" icon={plus(tokens.accentFg)} aria-label="Add" />
          <Fab placement="none" size="lg" icon={plus(tokens.accentFg, 34)} aria-label="Add" />
          <Fab placement="none" variant="secondary" icon={plus(tokens.accentStrong)} aria-label="Add" />
          <Fab placement="none" variant="surface" icon={plus(tokens.accent)} aria-label="Add" />
        </Row>
        <Row>
          <Fab placement="none" icon={plus(tokens.accentFg)} label="New invoice" />
          <Fab placement="none" loading icon={plus(tokens.accentFg)} aria-label="Saving" />
          <Fab placement="none" disabled icon={plus(tokens.accentFg)} aria-label="Add" />
        </Row>
      </Section>

      <Section title="Fab · extended, collapses while scrolling down">
        <MiniScreen>
          <ScrollView
            nestedScrollEnabled
            scrollEventThrottle={16}
            onScroll={(e) => setExtended(e.nativeEvent.contentOffset.y < 24)}
          >
            <FakeRows count={12} />
          </ScrollView>
          <Fab safeArea={false} icon={plus(tokens.accentFg)} label="New invoice" extended={extended} />
        </MiniScreen>
      </Section>

      <Section title="SpeedDial · above a BottomNavigation">
        <MiniScreen>
          <View style={{ flex: 1, padding: 16 }}>
            <Text style={{ color: tokens.muted }}>Last action: {last}</Text>
          </View>
          <SpeedDial
            safeArea={false}
            offset={64}
            aria-label="Create"
            icon={plus(tokens.accentFg)}
            actions={[
              { key: "invoice", label: "Invoice", icon: <TextIcon glyph="▤" color={tokens.accentStrong} />, onPress: () => setLast("Invoice") },
              { key: "payment", label: "Payment", icon: <TextIcon glyph="₹" color={tokens.accentStrong} />, onPress: () => setLast("Payment") },
              { key: "customer", label: "Customer", icon: <TextIcon glyph="◉" color={tokens.accentStrong} />, onPress: () => setLast("Customer") },
            ]}
          />
          <BottomNavigation
            safeArea={false}
            items={[
              { key: "home", label: "Home", icon: ({ color }) => <TextIcon glyph="⌂" color={color} /> },
              { key: "invoices", label: "Invoices", icon: ({ color }) => <TextIcon glyph="▤" color={color} /> },
              { key: "profile", label: "Profile", icon: ({ color }) => <TextIcon glyph="◉" color={color} /> },
            ]}
          />
        </MiniScreen>
      </Section>
    </>
  );
}
```

## Install

```bash
npx @grampro/kit@native add Fab
```

## Usage

```tsx
import { Fab, SpeedDial } from "@/component-lib/fab";

<Fab
  icon={<PlusIcon />}
  label="New invoice"
  extended={!scrolledDown}
  offset={64} // above a BottomNavigation
  onPress={create}
/>
```

## Props

### FabProps

| Prop | Type | Description |
|---|---|---|
| `size` | `FabSize` | Default `md` (56dp). `sm` is 40dp, `lg` 96dp. |
| `variant` | `FabVariant` | Default `primary`. |
| `icon` | `ReactNode` |   |
| `label` | `string` | Text next to the icon: an extended FAB. |
| `extended` | `boolean` | With a `label`: show it (true) or collapse to the icon (false), animated. Default true. |
| `loading` | `boolean` |   |
| `disabled` | `boolean` |   |
| `placement` | `FabPlacement` | Where it floats over its parent. Default `bottom-end`, which follows RTL. `none` leaves layout to you. |
| `offset` | `number` | Extra distance from the bottom, e.g. a BottomNavigation's height. Default 0. |
| `safeArea` | `boolean` | Respect safe-area insets when placed. Default true. |
| `onPress` | `(event: GestureResponderEvent) => void` |   |
| `style` | `StyleProp<ViewStyle>` |   |
| `styles` | `Partial<{ root: StyleProp<ViewStyle>; label: StyleProp<TextStyle>; icon: StyleProp<View…` |   |
| `localeText` | `Partial<FabLocaleText>` |   |

### SpeedDialProps

| Prop | Type | Description |
|---|---|---|
| `actions` (required) | `SpeedDialAction[]` |   |
| `icon` (required) | `ReactNode` | The main button's icon while closed. |
| `variant` | `FabVariant` |   |
| `placement` | `FabPlacement` |   |
| `offset` | `number` |   |
| `safeArea` | `boolean` |   |
| `open` | `boolean` |   |
| `onOpenChange` | `(open: boolean) => void` |   |
| `style` | `StyleProp<ViewStyle>` |   |
| `localeText` | `Partial<FabLocaleText>` |   |

## Notes

- Floats over its parent at `bottom-end` by default and follows RTL; `placement="none"` leaves layout to you.
- An icon-only Fab needs an `aria-label`. `extended={false}` collapses the label, animated.
- `SpeedDial` opens over a dimmed screen; Android back, a tap outside or ✕ closes it.
- Mobile only: there is no web version of this component.
