ProComponents, templates & AI tooling
2.3k

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.Background: Optional theme-aware background container rendered behind the button surface. Mounted automatically for the secondary and tertiary variants when the active theme registers default background content (e.g. glass). Replace or remove it via the background prop.
  • 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 accessibilityLabel="Like">
  <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 accessibilityLabel="Like">
          <Ionicons name="heart" size={16} color={themeColorAccentForeground} />
        </Button>
        <Button
          size="sm"
          variant="secondary"
          isIconOnly
          accessibilityLabel="Bookmark"
        >
          <Ionicons
            name="bookmark"
            size={16}
            color={themeColorAccentSoftForeground}
          />
        </Button>
        <Button
          size="sm"
          variant="danger"
          isIconOnly
          accessibilityLabel="Delete"
        >
          <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.

proptypedefaultdescription
variant'primary' | 'secondary' | 'tertiary' | 'outline' | 'ghost' | 'danger' | 'danger-soft''primary'Visual variant of the button
size'sm' | 'md' | 'lg''md'Size of the button
isIconOnlybooleanfalseWhether the button displays an icon only (square aspect ratio)
feedbackVariant'scale-highlight' | 'scale-ripple' | 'scale' | 'none''scale-highlight'Determines which feedback effects are rendered
animationButtonAnimation-Animation configuration (shape depends on feedbackVariant)
backgroundReact.ReactNode-Background layer behind the button surface. undefined renders the theme-aware default for the secondary and tertiary variants; custom node replaces it; null removes it

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:

  • true or undefined: Use default animations
  • false or "disabled": Disable all feedback animations
  • "disable-all": Cascade-disable all animations including child compound parts
  • object: Custom configuration with sub-animation keys (see below)

When feedbackVariant="scale-highlight" (default):

proptypedefaultdescription
scalePressableFeedbackScaleAnimation-Scale animation config (false to disable)
highlightPressableFeedbackHighlightAnimation-Highlight overlay config (false to disable)
state'disabled' | 'disable-all' | boolean-Control animation state while keeping config (runtime toggle)

When feedbackVariant="scale-ripple":

proptypedefaultdescription
scalePressableFeedbackScaleAnimation-Scale animation config (false to disable)
ripplePressableFeedbackRippleAnimation-Ripple overlay config (false to disable)
state'disabled' | 'disable-all' | boolean-Control animation state while keeping config (runtime toggle)

When feedbackVariant="scale":

proptypedefaultdescription
scalePressableFeedbackScaleAnimation-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.Background

Absolute-fill container rendered behind the button surface. With no children, the active library theme decides the default content (e.g. a glass blur layer); pass children to host custom content with the same positioning and clipping.

proptypedefaultdescription
childrenReact.ReactNode-Custom content inside the background container
classNamestring-Additional CSS classes
...ViewPropsViewProps-All standard View props are supported

Button.Label

proptypedefaultdescription
childrenReact.ReactNode-Content to be rendered as label
classNamestring-Additional CSS classes
...TextPropsTextProps-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

propertytypedescription
size'sm' | 'md' | 'lg'Size of the button
variant'primary' | 'secondary' | 'tertiary' | 'outline' | 'ghost' | 'danger' | 'danger-soft'Visual variant of the button
isDisabledbooleanWhether 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.

On this page