Skip to content

Button

Live preview

Hero actions and variant row — styled with RNUI semantic tokens.

Upgrade to Pro

Unlock offline mode, shared workspaces, and priority support for your team.

Start free trialLearn more
PrimaryOutlineGhostDelete

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
labelstring—Button text (optional for icon-only)
variant"solid" | "outline" | "ghost" | "destructive""solid"Visual style
size"sm" | "md" | "lg""md"Height and padding preset
onPress() => void—Press callback
onLongPress() => void—Long press callback
loadingbooleanfalseShow spinner, disable press
disabledbooleanfalseDisable interaction and dim
leadingIconReactNode—Slot before label (or center if no label)
trailingIconReactNode—Slot after label
feedbackMode"scale" | "scaleSubtle" | "opacity" | "none""scale"Press animation
fullWidthbooleanfalseFill container width
accessibilityLabelstringlabelOverride a11y label
accessibilityHintstring—Screen 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: 36px
  • Medium: 44px
  • Large: 52px

Default corner radius is radius.lg (12px) — rounded, not pill-shaped. Solid and accent variants use soft elevation; outline and ghost stay flat.

<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>
);
}