Skip to main content
Rainbow provides several button components with smooth press animations and haptic feedback for creating engaging user interfaces.

ButtonPressAnimation

A versatile button component with configurable press animations and haptic feedback.

Props

onPress
(event?: GestureResponderEvent) => void
Handler called when the button is pressed
onLongPress
(event?: GestureResponderEvent) => void
Handler called when the button is long-pressed
disabled
boolean
Whether the button is disabled
scaleTo
number
default:"0.96"
Scale value when pressed (e.g., 0.96 = 96% of original size)
activeOpacity
number
Opacity when pressed
duration
number
Animation duration in milliseconds
enableHapticFeedback
boolean
default:"true"
Whether to trigger haptic feedback on press
hapticType
HapticFeedbackTypes
Type of haptic feedback (e.g., “selection”, “impactLight”, “impactMedium”)
transformOrigin
TransformOrigin | Direction
Origin point for scale transformation. Can be coordinates [x, y] or direction (“top”, “bottom”, “left”, “right”)
onPressStart
() => void
Handler called when press gesture starts
onLongPressEnded
() => void
Handler called when long press ends
minLongPressDuration
number
Minimum duration for long press in milliseconds
hitSlop
Insets
Extends the touchable area
testID
string
Test identifier for automated testing

iOS-Specific Props

compensateForTransformOrigin
boolean
Compensates for transform origin changes
pressOutDuration
number
Duration of press release animation
useLateHaptic
boolean
Delays haptic feedback until press completes
isInteraction
boolean
Whether this press should be counted as a user interaction

Android-Specific Props

reanimatedButton
boolean
Use Reanimated-based implementation
backgroundColor
string
Background color for ripple effect
borderRadius
number
Border radius for ripple bounds
exclusive
boolean
Prevents multiple simultaneous presses

Usage

import ButtonPressAnimation from '@/components/animations/ButtonPressAnimation';
import { Box, Text } from '@/design-system';

function MyButton() {
  return (
    <ButtonPressAnimation
      onPress={() => console.log('Pressed!')}
      scaleTo={0.92}
      enableHapticFeedback
      hapticType="impactMedium"
    >
      <Box 
        background="blue" 
        padding="16px" 
        borderRadius={12}
      >
        <Text size="17pt" weight="bold" color="label">
          Press Me
        </Text>
      </Box>
    </ButtonPressAnimation>
  );
}

With Transform Origin

Control the origin point of the scale animation:
<ButtonPressAnimation
  onPress={handlePress}
  transformOrigin="bottom" // Scale from bottom
  scaleTo={0.95}
>
  <MyContent />
</ButtonPressAnimation>

// Or use coordinates [x, y]
<ButtonPressAnimation
  onPress={handlePress}
  transformOrigin={[0.5, 0]} // Center horizontally, top vertically
  scaleTo={0.95}
>
  <MyContent />
</ButtonPressAnimation>

With Long Press

<ButtonPressAnimation
  onPress={handlePress}
  onLongPress={handleLongPress}
  minLongPressDuration={500}
  onLongPressEnded={() => console.log('Long press ended')}
>
  <MyContent />
</ButtonPressAnimation>

SheetActionButton

Stylized action buttons commonly used in sheets and modals.

Props

label
string
Button label text
onPress
() => void
required
Press handler
color
string
Button background color
textColor
string
Label text color
size
'big' | 'small' | number
default:"'small'"
Button size preset or custom height
isSquare
boolean
Whether the button should be square (removes horizontal padding)
disabled
boolean
Whether the button is disabled
testID
string
Test identifier

Usage

import SheetActionButton from '@/components/sheet/sheet-action-buttons/SheetActionButton';

function MySheet() {
  return (
    <SheetActionButton
      label="Confirm"
      onPress={handleConfirm}
      color="#007AFF"
      textColor="#FFFFFF"
      size="big"
    />
  );
}

Sizes

// Small button (46px height)
<SheetActionButton
  label="Cancel"
  onPress={handleCancel}
  size="small"
/>

// Big button (52px height)
<SheetActionButton
  label="Confirm"
  onPress={handleConfirm}
  size="big"
/>

// Custom height
<SheetActionButton
  label="Custom"
  onPress={handlePress}
  size={60}
/>

Square Buttons

For icon-only buttons:
<SheetActionButton
  onPress={handleClose}
  isSquare
  size="small"
>
  <Icon name="close" size={20} />
</SheetActionButton>

SheetActionButtonRow

Container for multiple action buttons with consistent spacing.

Props

children
ReactNode
required
SheetActionButton components
ignorePaddingTop
boolean
Remove top padding
paddingBottom
number
Custom bottom padding

Usage

import { SheetActionButtonRow } from '@/components/sheet/sheet-action-buttons/SheetActionButtonRow';
import SheetActionButton from '@/components/sheet/sheet-action-buttons/SheetActionButton';

function ActionButtons() {
  return (
    <SheetActionButtonRow>
      <SheetActionButton
        label="Cancel"
        onPress={handleCancel}
        color="#8E8E93"
      />
      <SheetActionButton
        label="Confirm"
        onPress={handleConfirm}
        color="#007AFF"
        size="big"
      />
    </SheetActionButtonRow>
  );
}

Specialized Action Buttons

SendActionButton

Pre-configured button for send actions:
import { SendActionButton } from '@/components/sheet/sheet-action-buttons/SendActionButton';

<SendActionButton
  onPress={handleSend}
  disabled={!isValid}
/>

BuyActionButton

Pre-configured button for buy/purchase actions:
import { BuyActionButton } from '@/components/sheet/sheet-action-buttons/BuyActionButton';

<BuyActionButton
  onPress={handleBuy}
  disabled={!canBuy}
/>

SwapActionButton

Pre-configured button for swap actions:
import { SwapActionButton } from '@/components/sheet/sheet-action-buttons/SwapActionButton';

<SwapActionButton
  onPress={handleSwap}
  disabled={!canSwap}
/>

Common Patterns

Button with Loading State

import ButtonPressAnimation from '@/components/animations/ButtonPressAnimation';
import { Box, Text } from '@/design-system';
import Spinner from '@/components/Spinner';

function LoadingButton({ isLoading, onPress, label }) {
  return (
    <ButtonPressAnimation
      onPress={onPress}
      disabled={isLoading}
      scaleTo={0.96}
    >
      <Box 
        background="blue" 
        padding="16px" 
        borderRadius={12}
        alignItems="center"
        justifyContent="center"
      >
        {isLoading ? (
          <Spinner size={20} />
        ) : (
          <Text size="17pt" weight="bold" color="label">
            {label}
          </Text>
        )}
      </Box>
    </ButtonPressAnimation>
  );
}

Button with Icon

import ButtonPressAnimation from '@/components/animations/ButtonPressAnimation';
import { Box, Inline, Text } from '@/design-system';
import Icon from '@/components/icons/Icon';

function IconButton({ icon, label, onPress }) {
  return (
    <ButtonPressAnimation onPress={onPress} scaleTo={0.96}>
      <Box 
        background="surfaceSecondary" 
        padding="12px" 
        borderRadius={12}
      >
        <Inline space="8px" alignVertical="center">
          <Icon name={icon} size={20} />
          <Text size="15pt" weight="semibold" color="label">
            {label}
          </Text>
        </Inline>
      </Box>
    </ButtonPressAnimation>
  );
}

Disabled Button

<ButtonPressAnimation
  onPress={handlePress}
  disabled={isDisabled}
  activeOpacity={isDisabled ? 1 : 0.7}
>
  <Box 
    background={isDisabled ? 'fillTertiary' : 'blue'}
    padding="16px" 
    borderRadius={12}
  >
    <Text 
      size="17pt" 
      weight="bold" 
      color={isDisabled ? 'labelTertiary' : 'label'}
    >
      Submit
    </Text>
  </Box>
</ButtonPressAnimation>

Best Practices

1. Always Provide Haptic Feedback

Enable haptic feedback for better user experience:
// ✅ Good
<ButtonPressAnimation
  onPress={handlePress}
  enableHapticFeedback
  hapticType="impactMedium"
>

// ❌ Avoid
<ButtonPressAnimation
  onPress={handlePress}
  enableHapticFeedback={false}
>

2. Use Appropriate Scale Values

Subtle animations feel more polished:
// ✅ Subtle scale
scaleTo={0.96} // 4% reduction

// ❌ Too dramatic
scaleTo={0.8}  // 20% reduction

3. Disable During Async Operations

Prevent double-taps during async operations:
const [isLoading, setIsLoading] = useState(false);

const handlePress = async () => {
  setIsLoading(true);
  try {
    await someAsyncOperation();
  } finally {
    setIsLoading(false);
  }
};

<ButtonPressAnimation
  onPress={handlePress}
  disabled={isLoading}
>
  {/* Button content */}
</ButtonPressAnimation>

4. Provide Visual Feedback for Disabled State

Make disabled state obvious:
<ButtonPressAnimation
  onPress={handlePress}
  disabled={!isValid}
>
  <Box 
    background={!isValid ? 'fillTertiary' : 'blue'}
    padding="16px" 
    borderRadius={12}
    style={{ opacity: !isValid ? 0.5 : 1 }}
  >
    <Text 
      size="17pt" 
      weight="bold" 
      color={!isValid ? 'labelTertiary' : 'label'}
    >
      Continue
    </Text>
  </Box>
</ButtonPressAnimation>

Build docs developers (and LLMs) love