Button
A pressable button with variants, sizes, icons, and loading states.
import { Button } from "@/components/ui/button";
export function MyScreen() {
return (
<Button onPress={() => console.log("pressed")}>
Click me
</Button>
);
}Installation
npx @aniui/cli add buttonUsage
app/index.tsx
import { Button } from "@/components/ui/button";
export function MyScreen() {
return (
<Button onPress={() => console.log("pressed")}>
Click me
</Button>
);
}Variants
Six visual styles for different contexts and emphasis levels.
<Button variant="default">Default</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="destructive">Destructive</Button>
<Button variant="link">Link</Button>Sizes
<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>With Icons
Pass any React Native element as icon (leading) or iconAfter (trailing). Use size="icon" for icon-only buttons.
import { Ionicons } from "@expo/vector-icons";
// Icon before text
<Button icon={<Ionicons name="add" size={18} color="#fff" />}>
Create
</Button>
// Icon after text
<Button iconAfter={<Ionicons name="send" size={18} color="#fff" />}>
Send
</Button>
// Icon only
<Button size="icon" icon={<Ionicons name="heart" size={18} color="#fff" />} />Loading
Set loading to show a spinner and disable interaction. The spinner color adapts to the variant.
<Button loading>Saving...</Button>
<Button loading variant="outline">Loading</Button>
<Button loading variant="destructive">Deleting</Button>Disabled
<Button disabled>Disabled</Button>
<Button disabled variant="outline">Disabled</Button>Full Width
Add className="w-full" for block-level buttons.
<Button className="w-full">Full Width Button</Button>
<Button className="w-full" variant="outline">Full Width Outline</Button>Props
PropTypeDefault
variant"default" | "secondary" | "outline" | "ghost" | "destructive" | "link""default"size"sm" | "md" | "lg" | "icon""md"iconReact.ReactNode—iconAfterReact.ReactNode—loadingbooleanfalsedisabledbooleanfalseclassNamestring—textClassNamestring—childrenstring—Also accepts all Pressable props from React Native.
Source
components/ui/button.tsx
import React from "react";
import { Pressable, Text, ActivityIndicator } from "react-native";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
const buttonVariants = cva(
"flex-row items-center justify-center rounded-md min-h-12 min-w-12",
{
variants: {
variant: {
default: "bg-primary",
secondary: "bg-secondary",
outline: "border border-input bg-transparent",
ghost: "bg-transparent",
destructive: "bg-destructive",
link: "bg-transparent",
},
size: {
sm: "px-3 py-1.5 gap-1.5",
md: "px-4 py-2.5 gap-2",
lg: "px-6 py-3.5 gap-2.5",
icon: "h-10 w-10 p-0",
},
},
defaultVariants: { variant: "default", size: "md" },
}
);
const buttonTextVariants = cva("text-center font-medium", {
variants: {
variant: {
default: "text-primary-foreground",
secondary: "text-secondary-foreground",
outline: "text-foreground",
ghost: "text-foreground",
destructive: "text-destructive-foreground",
link: "text-primary underline",
},
size: { sm: "text-sm", md: "text-base", lg: "text-lg", icon: "text-sm" },
},
defaultVariants: { variant: "default", size: "md" },
});
export interface ButtonProps
extends React.ComponentPropsWithoutRef<typeof Pressable>,
VariantProps<typeof buttonVariants> {
className?: string;
textClassName?: string;
children?: string;
icon?: React.ReactNode;
iconAfter?: React.ReactNode;
loading?: boolean;
}
export function Button({ variant, size, className, textClassName, children, icon, iconAfter, loading, disabled, ...props }: ButtonProps) {
const isDisabled = disabled || loading;
const light = variant === "default" || variant === "destructive";
return (
<Pressable
className={cn(buttonVariants({ variant, size }), isDisabled && "opacity-50", className)}
accessibilityRole="button"
accessible={true}
disabled={isDisabled}
{...props}
>
{loading ? (
<ActivityIndicator size="small" color={light ? "hsl(0,0%,98%)" : "hsl(240,5.9%,10%)"} />
) : icon ?? null}
{children ? (
<Text className={cn(buttonTextVariants({ variant, size }), textClassName)}>{children}</Text>
) : null}
{!loading && iconAfter ? iconAfter : null}
</Pressable>
);
}