Rainbow provides several button components with smooth press animations and haptic feedback for creating engaging user interfaces.
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
Whether the button is disabled
Scale value when pressed (e.g., 0.96 = 96% of original size)
Animation duration in milliseconds
Whether to trigger haptic feedback on press
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”)
Handler called when press gesture starts
Handler called when long press ends
Minimum duration for long press in milliseconds
Extends the touchable area
Test identifier for automated testing
iOS-Specific Props
compensateForTransformOrigin
Compensates for transform origin changes
Duration of press release animation
Delays haptic feedback until press completes
Whether this press should be counted as a user interaction
Android-Specific Props
Use Reanimated-based implementation
Background color for ripple effect
Border radius for ripple bounds
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>
);
}
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>
Stylized action buttons commonly used in sheets and modals.
Props
size
'big' | 'small' | number
default:"'small'"
Button size preset or custom height
Whether the button should be square (removes horizontal padding)
Whether the button is disabled
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}
/>
For icon-only buttons:
<SheetActionButton
onPress={handleClose}
isSquare
size="small"
>
<Icon name="close" size={20} />
</SheetActionButton>
Container for multiple action buttons with consistent spacing.
Props
SheetActionButton components
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>
);
}
Pre-configured button for send actions:
import { SendActionButton } from '@/components/sheet/sheet-action-buttons/SendActionButton';
<SendActionButton
onPress={handleSend}
disabled={!isValid}
/>
Pre-configured button for buy/purchase actions:
import { BuyActionButton } from '@/components/sheet/sheet-action-buttons/BuyActionButton';
<BuyActionButton
onPress={handleBuy}
disabled={!canBuy}
/>
Pre-configured button for swap actions:
import { SwapActionButton } from '@/components/sheet/sheet-action-buttons/SwapActionButton';
<SwapActionButton
onPress={handleSwap}
disabled={!canSwap}
/>
Common Patterns
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>
);
}
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>
);
}
<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>