Skip to content

Button

The Button component wraps usePressable from the headless layer. All animation runs on the UI thread via Reanimated 3.

import { Button } from '@truongdq01/ui';
<Button label="Save changes" variant="solid" onPress={handleSave} />;
PropTypeDefaultDescription
labelstringButton text (optional for icon-only)
variant"solid" | "outline" | "ghost" | "destructive""solid"Visual style
size"sm" | "md" | "lg""md"Height and padding preset
onPress() => voidPress callback
onLongPress() => voidLong press callback
loadingbooleanfalseShow spinner, disable press
disabledbooleanfalseDisable interaction and dim
leadingIconReactNodeSlot before label (or center if no label)
trailingIconReactNodeSlot after label
feedbackMode"scale" | "scaleSubtle" | "opacity" | "none""scale"Press animation
fullWidthbooleanfalseFill container width
accessibilityLabelstringlabelOverride a11y label
accessibilityHintstringScreen reader hint
<Button label="Primary" variant="solid" onPress={() => {}} />
<Button label="Secondary" variant="outline" onPress={() => {}} />
<Button label="Tertiary" variant="ghost" onPress={() => {}} />
<Button label="Delete" variant="destructive" onPress={() => {}} />

Buttons come in three standardized heights:

  • Small: 32px
  • Medium: 44px
  • Large: 54px
<Button label="Small" size="sm" onPress={() => {}} />
<Button label="Medium" size="md" onPress={() => {}} /> {/* default */}
<Button label="Large" size="lg" onPress={() => {}} />
<Button label="Saving…" loading={isSaving} onPress={save} />

The library uses a standardized icon styling system. Icons passed to leadingIcon or trailingIcon will automatically receive the correct size and color based on the button’s variant and size.

import { Plus, Send, Mail } from "lucide-react-native";
// Leading icon
<Button label="Add item" leadingIcon={<Plus />} />
// Trailing icon
<Button label="Send" variant="outline" trailingIcon={<Send />} />
// Icon only
<Button leadingIcon={<Mail />} onPress={() => {}} />
import { usePressable } from '@truongdq01/headless';
import Animated from 'react-native-reanimated';
import { GestureDetector } from 'react-native-gesture-handler';
function MyButton({ label, onPress }) {
const { tokens } = useTheme();
const { gesture, animatedStyle, accessibilityProps } = usePressable({
onPress,
feedbackMode: 'scale',
accessibilityLabel: label,
});
return (
<GestureDetector gesture={gesture}>
<Animated.View style={[myStyle, animatedStyle]} {...accessibilityProps}>
<Text>{label}</Text>
</Animated.View>
</GestureDetector>
);
}