Skip to content
React NativeBetaMobile only

Fab & SpeedDial

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

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

On this page
Web preview — real device may differ
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

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

PropTypeDescription
sizeFabSizeDefault md (56dp). sm is 40dp, lg 96dp.
variantFabVariantDefault primary.
iconReactNode
labelstringText next to the icon: an extended FAB.
extendedbooleanWith a label: show it (true) or collapse to the icon (false), animated. Default true.
loadingboolean
disabledboolean
placementFabPlacementWhere it floats over its parent. Default bottom-end, which follows RTL. none leaves layout to you.
offsetnumberExtra distance from the bottom, e.g. a BottomNavigation's height. Default 0.
safeAreabooleanRespect safe-area insets when placed. Default true.
onPress(event: GestureResponderEvent) => void
styleStyleProp<ViewStyle>
stylesPartial<{ root: StyleProp<ViewStyle>; label: StyleProp<TextStyle>; icon: StyleProp<View…
localeTextPartial<FabLocaleText>

SpeedDialProps

PropTypeDescription
actions (required)SpeedDialAction[]
icon (required)ReactNodeThe main button's icon while closed.
variantFabVariant
placementFabPlacement
offsetnumber
safeAreaboolean
openboolean
onOpenChange(open: boolean) => void
styleStyleProp<ViewStyle>
localeTextPartial<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.