Button
Interactive component that triggers an action when pressed.
Import
import { Button } from 'heroui-native';Anatomy
<Button>
<Button.Label>...</Button.Label>
</Button>- Button: Main container that handles press interactions, animations, and variants. Renders string children as label or accepts compound components for custom layouts.
- Button.Label: Text content of the button. Inherits size and variant styling from parent Button context.
Usage
Basic Usage
The Button component accepts string children that automatically render as label.
<Button>Basic Button</Button>With Compound Parts
Use Button.Label for explicit control over the label component.
<Button>
<Button.Label>Click me</Button.Label>
</Button>With Icons
Combine icons with labels for enhanced visual communication.
<Button>
<Icon name="add" size={20} />
<Button.Label>Add Item</Button.Label>
</Button>
<Button>
<Button.Label>Download</Button.Label>
<Icon name="download" size={18} />
</Button>Icon Only
Create square icon-only buttons using the isIconOnly prop.
<Button isIconOnly>
<Icon name="heart" size={18} />
</Button>Sizes
Control button dimensions with three size options.
<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>Variants
Choose from seven visual variants for different emphasis levels.
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="tertiary">Tertiary</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="danger">Danger</Button>
<Button variant="danger-soft">Danger Soft</Button>Feedback Variants
The feedbackVariant prop controls which press feedback effects are rendered:
'scale-highlight'(default): Built-in scale + highlight overlay'scale-ripple': Built-in scale + ripple overlay'scale': Built-in scale only (no overlay)'none': No feedback animations at all
{/* Scale + Highlight (default) */}
<Button feedbackVariant="scale-highlight">Highlight Effect</Button>
{/* Scale + Ripple */}
<Button feedbackVariant="scale-ripple">Ripple Effect</Button>
{/* Scale only */}
<Button feedbackVariant="scale">Scale Only</Button>
{/* No feedback */}
<Button feedbackVariant="none">No Feedback</Button>Custom Animation
The animation prop controls individual sub-animations. Its shape depends on the feedbackVariant.
{/* Customize scale and highlight (default feedbackVariant) */}
<Button
animation={{
scale: { value: 0.97 },
highlight: {
backgroundColor: { value: '#3b82f6' },
opacity: { value: [0, 0.2] },
},
}}
>
Custom Highlight
</Button>
{/* Customize scale and ripple */}
<Button
feedbackVariant="scale-ripple"
animation={{
scale: { value: 0.97 },
ripple: {
backgroundColor: { value: '#3b82f6' },
opacity: { value: [0, 0.3, 0] },
},
}}
>
Custom Ripple
</Button>Disable Individual Animations
Disable specific sub-animations by setting them to false:
{/* Disable scale, keep highlight */}
<Button animation={{ scale: false }}>No Scale</Button>
{/* Disable highlight, keep scale */}
<Button animation={{ highlight: false }}>No Highlight</Button>
{/* Disable both */}
<Button animation={{ scale: false, highlight: false }}>No Animations</Button>Disable All Animations
Use animation={false} to disable all feedback, or animation="disable-all" for cascading disable:
<Button animation={false}>Disabled</Button>
<Button animation="disable-all">Disable All (cascading)</Button>Loading State with Spinner
Transform button to loading state with spinner animation.
const themeColorAccentForeground = useThemeColor('accent-foreground');
<Button
layout={LinearTransition.springify()}
variant="primary"
onPress={() => {
setIsDownloading(true);
setTimeout(() => {
setIsDownloading(false);
}, 3000);
}}
isIconOnly={isDownloading}
className="self-center"
>
{isDownloading ? (
<Spinner entering={FadeIn.delay(50)} color={themeColorAccentForeground} />
) : (
'Download now'
)}
</Button>;Custom Background with LinearGradient
Add gradient backgrounds using absolute positioned elements. Use feedbackVariant="none" to disable the default highlight overlay, or use feedbackVariant="scale-ripple" for a custom ripple effect.
import { Button, PressableFeedback } from 'heroui-native';
import { LinearGradient } from 'expo-linear-gradient';
import { StyleSheet } from 'react-native';
{/* Gradient with no feedback overlay */}
<Button feedbackVariant="none">
<LinearGradient
colors={['#9333ea', '#ec4899']}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 0 }}
style={StyleSheet.absoluteFill}
/>
<Button.Label className="text-white font-bold">Gradient</Button.Label>
</Button>
{/* Gradient with custom ripple effect */}
<Button
feedbackVariant="scale-ripple"
animation={{
ripple: {
backgroundColor: { value: 'white' },
opacity: { value: [0, 0.5, 0] },
},
}}
>
<LinearGradient
colors={['#0d9488', '#ec4899']}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 0 }}
style={StyleSheet.absoluteFill}
/>
<Button.Label className="text-white font-bold" pointerEvents="none">
Gradient with Ripple
</Button.Label>
</Button>Example
import { Button, useThemeColor } from 'heroui-native';
import { Ionicons } from '@expo/vector-icons';
import { View } from 'react-native';
export default function ButtonExample() {
const [
themeColorAccentForeground,
themeColorAccentSoftForeground,
themeColorDangerForeground,
themeColorDefaultForeground,
] = useThemeColor([
'accent-foreground',
'accent-soft-foreground',
'danger-foreground',
'default-foreground',
]);
return (
<View className="gap-4 p-4">
<Button variant="primary">
<Ionicons name="add" size={20} color={themeColorAccentForeground} />
<Button.Label>Add Item</Button.Label>
</Button>
<View className="flex-row gap-4">
<Button size="sm" isIconOnly>
<Ionicons name="heart" size={16} color={themeColorAccentForeground} />
</Button>
<Button size="sm" variant="secondary" isIconOnly>
<Ionicons
name="bookmark"
size={16}
color={themeColorAccentSoftForeground}
/>
</Button>
<Button size="sm" variant="danger" isIconOnly>
<Ionicons name="trash" size={16} color={themeColorDangerForeground} />
</Button>
</View>
<Button variant="tertiary">
<Button.Label>Learn More</Button.Label>
<Ionicons
name="chevron-forward"
size={18}
color={themeColorDefaultForeground}
/>
</Button>
</View>
);
}You can find more examples in the GitHub repository.
API Reference
Button
Button extends all props from PressableFeedback (except animation, which is redefined) with additional button-specific props.
| prop | type | default | description |
|---|---|---|---|
variant | 'primary' | 'secondary' | 'tertiary' | 'outline' | 'ghost' | 'danger' | 'danger-soft' | 'primary' | Visual variant of the button |
size | 'sm' | 'md' | 'lg' | 'md' | Size of the button |
isIconOnly | boolean | false | Whether the button displays an icon only (square aspect ratio) |
feedbackVariant | 'scale-highlight' | 'scale-ripple' | 'scale' | 'none' | 'scale-highlight' | Determines which feedback effects are rendered |
animation | ButtonAnimation | - | Animation configuration (shape depends on feedbackVariant) |
For inherited props including isDisabled, className, children, and all Pressable props, see PressableFeedback API Reference.
ButtonAnimation
The animation prop is a discriminated union based on feedbackVariant. It follows the AnimationRoot control flow:
trueorundefined: Use default animationsfalseor"disabled": Disable all feedback animations"disable-all": Cascade-disable all animations including child compound partsobject: Custom configuration with sub-animation keys (see below)
When feedbackVariant="scale-highlight" (default):
| prop | type | default | description |
|---|---|---|---|
scale | PressableFeedbackScaleAnimation | - | Scale animation config (false to disable) |
highlight | PressableFeedbackHighlightAnimation | - | Highlight overlay config (false to disable) |
state | 'disabled' | 'disable-all' | boolean | - | Control animation state while keeping config (runtime toggle) |
When feedbackVariant="scale-ripple":
| prop | type | default | description |
|---|---|---|---|
scale | PressableFeedbackScaleAnimation | - | Scale animation config (false to disable) |
ripple | PressableFeedbackRippleAnimation | - | Ripple overlay config (false to disable) |
state | 'disabled' | 'disable-all' | boolean | - | Control animation state while keeping config (runtime toggle) |
When feedbackVariant="scale":
| prop | type | default | description |
|---|---|---|---|
scale | PressableFeedbackScaleAnimation | - | Scale animation config (false to disable) |
state | 'disabled' | 'disable-all' | boolean | - | Control animation state while keeping config (runtime toggle) |
When feedbackVariant="none":
Only 'disable-all' is accepted as a string value. All feedback effects are disabled.
For detailed animation sub-types (PressableFeedbackScaleAnimation, PressableFeedbackHighlightAnimation, PressableFeedbackRippleAnimation), see PressableFeedback API Reference.
Button.Label
| prop | type | default | description |
|---|---|---|---|
children | React.ReactNode | - | Content to be rendered as label |
className | string | - | Additional CSS classes |
...TextProps | TextProps | - | All standard Text props are supported |
Hooks
useButton
Hook to access the Button context values. Returns the button's size, variant, and disabled state.
import { useButton } from 'heroui-native';
const { size, variant, isDisabled } = useButton();Return Value
| property | type | description |
|---|---|---|
size | 'sm' | 'md' | 'lg' | Size of the button |
variant | 'primary' | 'secondary' | 'tertiary' | 'outline' | 'ghost' | 'danger' | 'danger-soft' | Visual variant of the button |
isDisabled | boolean | Whether the button is disabled |
Note: This hook must be used within a Button component. It will throw an error if called outside of the button context.