A floating action button that expands into a list of actions with automatic content placement and a shared, progress-driven open/close animation.
import { FAB } from 'heroui-native-pro' ;
< FAB className = "absolute bottom-6 right-6" >
< FAB.Trigger >...</ FAB.Trigger >
< FAB.Portal >
< FAB.Overlay />
< FAB.Content >
< FAB.Item onPress = { ... }>New message</ FAB.Item >
< FAB.Item onPress = { ... }>
< ShareIcon />
< FAB.ItemLabel >Share</ FAB.ItemLabel >
</ FAB.Item >
</ FAB.Content >
</ FAB.Portal >
</ FAB >
FAB : Root container. Owns the open state (controlled via isOpen + onOpenChange or uncontrolled via isDefaultOpen), resolves the content placement and alignment — automatically from the trigger position on screen by default — and drives the shared open/close progress (0 = idle, 1 = open, 2 = close) that orchestrates the overlay, items, and trigger rotation. Cascades disable-all to animated descendants.
FAB.Trigger : The floating button itself. Toggles the open state on press and measures its own position so auto placement can resolve. Its content rotates with the shared progress (a plus icon reads as a close affordance while open). Position the FAB by passing positioning classes (e.g. absolute bottom-6 right-6) to the root.
FAB.Portal : Renders the overlay and content in a portal layer above other content (using FullWindowOverlay on iOS). Stays mounted while the close animation plays and re-provides the FAB contexts to portaled descendants.
FAB.Overlay : Optional backdrop behind the content. Its opacity follows the shared progress and pressing it closes the FAB. Replace it with a custom component built on useFABAnimation (e.g. a blur backdrop) for custom backdrops.
FAB.Content : Positioned column of items. Placement and alignment follow the root resolution and the column hugs the trigger edge. Provides each child its index so items can stagger.
FAB.Item : Single action row. Appears and disappears with the shared progress — staggered by default, starting from the item nearest the trigger — and closes the FAB on press unless closeOnPress={false}. Plain string children are wrapped in FAB.ItemLabel automatically.
FAB.ItemLabel : Text label inside an item. Only needed for custom layouts (e.g. icon + label); string children get it for free.
Place the FAB anywhere on screen; the content placement resolves automatically. A FAB in the bottom-right corner opens its items upwards with end alignment, a FAB in the top-left opens downwards with start alignment, and so on.
< FAB >
< FAB.Trigger >
< PlusIcon />
</ FAB.Trigger >
< FAB.Portal >
< FAB.Overlay />
< FAB.Content >
< FAB.Item onPress = {handleNewMessage}>New message</ FAB.Item >
< FAB.Item onPress = {handleNewLabel}>New label</ FAB.Item >
</ FAB.Content >
</ FAB.Portal >
</ FAB >
Override the automatic resolution with explicit placement and align values on the root.
< FAB placement = "bottom" align = "start" >
< FAB.Trigger >...</ FAB.Trigger >
< FAB.Portal >
< FAB.Overlay />
< FAB.Content >...</ FAB.Content >
</ FAB.Portal >
</ FAB >
Items appear staggered by default (nearest to the trigger first, reversed on close). Set itemsAppearance="normal" to animate all items together.
< FAB itemsAppearance = "normal" >
< FAB.Trigger >...</ FAB.Trigger >
< FAB.Portal >
< FAB.Overlay />
< FAB.Content >...</ FAB.Content >
</ FAB.Portal >
</ FAB >
Control the stagger intensity with animation.stagger.itemWindow on the root: the fraction of the progress range each item's animation occupies. Smaller values produce a more sequential stagger; 1 makes all items animate together.
< FAB animation = {{ stagger: { itemWindow: 0.4 } }}>
< FAB.Trigger >...</ FAB.Trigger >
< FAB.Portal >
< FAB.Overlay />
< FAB.Content >...</ FAB.Content >
</ FAB.Portal >
</ FAB >
Pass elements as item children for custom layouts. Use FAB.ItemLabel for the text part.
< FAB.Item onPress = {handleShare}>
< ShareIcon size = { 16 } />
< FAB.ItemLabel >Share</ FAB.ItemLabel >
</ FAB.Item >
Drive the open state externally with isOpen and onOpenChange.
const [ isOpen , setIsOpen ] = useState ( false );
< FAB isOpen = {isOpen} onOpenChange = {setIsOpen}>
< FAB.Trigger >...</ FAB.Trigger >
< FAB.Portal >
< FAB.Overlay />
< FAB.Content >...</ FAB.Content >
</ FAB.Portal >
</ FAB >;
Build a custom backdrop on the shared progress via useFABAnimation and place it inside FAB.Portal instead of FAB.Overlay. The progress follows the [idle, open, close] = [0, 1, 2] convention.
import { useFAB, useFABAnimation } from 'heroui-native-pro' ;
import { BlurView } from 'expo-blur' ;
import { Pressable, StyleSheet } from 'react-native' ;
import Animated, {
interpolate,
useAnimatedProps,
} from 'react-native-reanimated' ;
const AnimatedBlurView = Animated. createAnimatedComponent (BlurView);
function FABBlurBackdrop () {
const { onOpenChange } = useFAB ();
const { progress } = useFABAnimation ();
const animatedProps = useAnimatedProps (() => ({
intensity: interpolate (progress. get (), [ 0 , 1 , 2 ], [ 0 , 50 , 0 ]),
}));
return (
< Pressable
style = {StyleSheet.absoluteFill}
onPress = {() => onOpenChange ( false )}
>
< AnimatedBlurView
animatedProps = {animatedProps}
style = {StyleSheet.absoluteFill}
/>
</ Pressable >
);
}
< FAB.Portal >
< FABBlurBackdrop />
< FAB.Content >...</ FAB.Content >
</ FAB.Portal >;
Customize the progress driver on the root (a spring with { mass: 3, stiffness: 1200, damping: 90 } unless configured) and the per-part motion on each part.
< FAB
animation = {{
progress: { type: 'timing' , config: { duration: 400 } },
// or: progress: { type: 'spring', config: { damping: 14, stiffness: 160 } }
}}
>
< FAB.Trigger animation = {{ rotate: { value: [ 0 , 135 , 0 ] } }}>...</ FAB.Trigger >
< FAB.Portal >
< FAB.Overlay animation = {{ opacity: { value: [ 0 , 0.6 , 0 ] } }} />
< FAB.Content >
< FAB.Item animation = {{ translate: { value: 24 }, scale: { value: [ 0.8 , 1 ] } }}>
...
</ FAB.Item >
</ FAB.Content >
</ FAB.Portal >
</ FAB >
import { Ionicons } from '@expo/vector-icons' ;
import { FAB } from 'heroui-native-pro' ;
import { Text, View } from 'react-native' ;
const MESSAGES = [
{
sender: 'Maya Chen' ,
subject: 'Design review notes' ,
time: '09:41' ,
},
{
sender: 'Hero Bank' ,
subject: 'Your statement is ready' ,
time: '08:15' ,
},
{
sender: 'Luis Ortega' ,
subject: 'Re: Offsite agenda' ,
time: 'Yesterday' ,
},
];
export default function InboxComposeFab () {
return (
< View className = "flex-1 bg-surface" >
< View className = "px-5 pt-5 pb-3 border-b border-border" >
< Text className = "text-xl font-bold text-foreground" >Inbox</ Text >
< Text className = "text-xs text-muted" >3 unread messages</ Text >
</ View >
< View className = "flex-1" >
{ MESSAGES . map (( message ) => (
< View
key = {message.subject}
className = "px-5 py-3.5 border-b border-default-soft"
>
< View className = "flex-row items-center justify-between" >
< Text className = "text-sm font-semibold text-foreground" >
{message.sender}
</ Text >
< Text className = "text-xs text-muted" >{message.time}</ Text >
</ View >
< Text className = "text-xs text-foreground" numberOfLines = { 1 }>
{message.subject}
</ Text >
</ View >
))}
</ View >
< FAB className = "absolute bottom-6 right-6" >
< FAB.Trigger >
< Ionicons name = "pencil" size = { 22 } color = "white" />
</ FAB.Trigger >
< FAB.Portal >
< FAB.Overlay />
< FAB.Content >
< FAB.Item onPress = {() => console. log ( 'new message' )}>
New message
</ FAB.Item >
< FAB.Item onPress = {() => console. log ( 'new label' )}>
New label
</ FAB.Item >
< FAB.Item onPress = {() => console. log ( 'new folder' )}>
< Ionicons name = "folder-outline" size = { 16 } color = "gray" />
< FAB.ItemLabel >New folder</ FAB.ItemLabel >
</ FAB.Item >
</ FAB.Content >
</ FAB.Portal >
</ FAB >
</ View >
);
}
prop type default description childrenReact.ReactNode- Compound parts to render (FAB.Trigger + FAB.Portal) placementFABAutoPlacement"auto"Side of the trigger on which the content opens. "auto" resolves from the trigger screen position alignFABAutoAlign"auto"Alignment along the perpendicular axis. "auto" resolves from the trigger screen position itemsAppearanceFABItemsAppearance"staggered"Appearing mode for the items ("staggered" or "normal") isOpenboolean- Whether the FAB is open (controlled mode) isDefaultOpenboolean- Default open state for uncontrolled mode isDisabledboolean- Whether the FAB is disabled classNamestring- Additional CSS classes for the root container (use for positioning, e.g. absolute bottom-6 right-6) onOpenChange(isOpen: boolean) => void- Callback fired when the open state changes animationFABRootAnimation- Animation configuration for the progress (spring/timing driver, stagger, and disable-all cascade) ...ViewPropsViewProps- All standard React Native View props are supported
Side of the trigger on which the content is rendered.
"auto": Resolves from the trigger position — a trigger in the bottom half of the screen opens upwards, a trigger in the top half opens downwards
"top" / "bottom" / "left" / "right": Explicit placement
Alignment of the content along the axis perpendicular to the placement.
"auto": Resolves from the trigger position — the perpendicular axis is split into thirds mapping to "start", "center", and "end"
"start" / "center" / "end": Explicit alignment
"staggered": Items appear one after another, nearest to the trigger first; the order is reversed on close
"normal": All items appear and disappear together
Animation configuration for the root component. Can be:
false or "disabled": Disable only root animations (open/close snaps instantly; children can still animate)
"disable-all": Disable all animations including children (cascades down through AnimationSettingsProvider)
true or undefined: Use default animations
object: Custom animation configuration
prop type default description progressAnimationValue- Configuration for the open/close progress staggerAnimationValue- Configuration for the item stagger (staggered items appearance only)
prop type default description type"spring" | "timing""spring"Driver animating the progress (0 = idle, 1 = open, 2 = close) configWithSpringConfig | WithTimingConfig{ mass: 3, stiffness: 1200, damping: 90 }Configuration for the chosen driver. A custom spring config replaces the default; timing configs use Reanimated defaults when omitted
prop type default description itemWindownumber0.5Fraction of the progress range each item's animation occupies, clamped to (0, 1]. Smaller values produce a more sequential stagger; 1 makes all items animate together
prop type default description childrenReact.ReactNode- Content rendered inside the trigger (typically an icon) isDisabledbooleanfalseWhether the trigger is disabled classNamestring- Additional CSS classes for the trigger container classNamesElementSlots<TriggerSlots>- Additional CSS classes for the trigger slots (container, contentContainer) stylesPartial<Record<TriggerSlots, ViewStyle>>- Additional styles for the trigger slots animationFABTriggerAnimation- Animation configuration for the content rotation isAnimatedStyleActivebooleantrueWhether animated styles are active ...PressablePropsPressableProps- All standard React Native Pressable props are supported
prop type default description rotateAnimationValue- Rotation of the trigger content
prop type default description value[number, number, number][0, 45, 0]Rotation degrees for the [idle, open, close] progress states
The trigger ref exposes imperative methods:
method description open()Programmatically open the FAB close()Programmatically close the FAB
prop type default description childrenReact.ReactNode- Content to render within the portal hostNamestring- Optional name of the portal host to render into forceMounttrue- Force mount the portal regardless of the open state disableFullWindowOverlaybooleanfalseUse a regular View instead of FullWindowOverlay on iOS unstable_accessibilityContainerViewIsModalbooleanfalseWhether VoiceOver treats the overlay window as a modal container (iOS) classNamestring- Additional CSS classes for the portal container
prop type default description closeOnPressbooleantrueWhether pressing the overlay closes the FAB classNamestring- Additional CSS classes for the overlay animationFABOverlayAnimation- Animation configuration for the overlay opacity isAnimatedStyleActivebooleantrueWhether animated styles are active ...PressablePropsPressableProps- All standard React Native Pressable props are supported
prop type default description opacityAnimationValue- Opacity of the overlay
prop type default description value[number, number, number][0, 1, 0]Opacity values for the [idle, open, close] progress states
prop type default description childrenReact.ReactNode- Items to render, typically FAB.Item parts offsetnumber12Gap between the trigger and the content in pixels alignOffsetnumber0Offset along the alignment axis in pixels insetsInsets{ top: 12, bottom: 12, left: 12, right: 12 }Screen edge insets respected when positioning avoidCollisionsbooleantrueWhether to adjust position to avoid screen edges disablePositioningStylebooleanfalseDisable the automatic positioning styles classNamestring- Additional CSS classes for the content container ...ViewPropsViewProps- All standard React Native View props are supported
prop type default description childrenReact.ReactNode- Item content. Strings/numbers are wrapped in FAB.ItemLabel automatically closeOnPressbooleantrueWhether pressing the item closes the FAB classNamestring- Additional CSS classes for the item container animationFABItemAnimation- Animation configuration for the appearing motion isAnimatedStyleActivebooleantrueWhether animated styles are active ...PressablePropsPressableProps- All standard React Native Pressable props are supported
prop type default description translateAnimationValue- Translation of the item towards the trigger scaleAnimationValue- Scale of the item while appearing
prop type default description valuenumber16Distance in pixels the item travels from the trigger direction
prop type default description value[number, number][0.9, 1]Scale values for the [hidden, visible] states
prop type default description childrenReact.ReactNode- Label text classNamestring- Additional CSS classes for the label ...TextPropsTextProps- All standard React Native Text props are supported
Hook to access the FAB root context. Must be used within a FAB component (works inside FAB.Portal too).
import { useFAB } from 'heroui-native-pro' ;
const { isOpen , onOpenChange , placement , align } = useFAB ();
property type description isOpenbooleanWhether the FAB is currently open onOpenChange(isOpen: boolean) => voidCallback to change the open state triggerPositionLayoutPosition | nullMeasured trigger position (page coordinates) contentLayoutLayoutRectangle | nullMeasured content layout placementFABPlacementResolved content placement alignFABAlignResolved content alignment nativeIDstringUnique identifier for the FAB instance
Hook to access the shared open/close progress. Use it to build custom progress-driven parts (e.g. a blur backdrop). Must be used within a FAB component (works inside FAB.Portal too).
import { useFABAnimation } from 'heroui-native-pro' ;
const { progress } = useFABAnimation ();
property type description progressSharedValue<number>Animated open/close progress (0 = idle/closed, 1 = open, 2 = close target; resets to 0 after close)