Button
A pressable action with variants and sizes.
- Chromium
- Firefox
- WebKit
- WCAG 2.2 AA
Preview
"use client";
import { Button } from "@moe-ui/registry/components/ui/button";
import { Text } from "@moe-ui/registry/components/ui/text";
export const ButtonPreview = () => {
return (
<Button>
<Text>Button</Text>
</Button>
);
};
One command, your source
Installation
pnpm dlx @moe-ui/cli@beta add buttonUsage
The CLI installs editable source into your application. Import the component from your configured UI directory:
import { Button } from "@/components/ui/button";Variants and composition
Each additional variant has its own live preview and testable section.
Secondary
"use client";
import { Button } from "@moe-ui/registry/components/ui/button";
import { Text } from "@moe-ui/registry/components/ui/text";
export const ButtonSecondaryVariantPreview = () => {
return (
<Button variant="secondary">
<Text>Secondary</Text>
</Button>
);
};
Destructive
"use client";
import { Button } from "@moe-ui/registry/components/ui/button";
import { Text } from "@moe-ui/registry/components/ui/text";
export const ButtonDestructiveVariantPreview = () => {
return (
<Button variant="destructive">
<Text>Destructive</Text>
</Button>
);
};
Outline
"use client";
import { Button } from "@moe-ui/registry/components/ui/button";
import { Text } from "@moe-ui/registry/components/ui/text";
export const ButtonOutlineVariantPreview = () => {
return (
<Button variant="outline">
<Text>Outline</Text>
</Button>
);
};
Ghost
"use client";
import { Button } from "@moe-ui/registry/components/ui/button";
import { Text } from "@moe-ui/registry/components/ui/text";
export const ButtonGhostVariantPreview = () => {
return (
<Button variant="ghost">
<Text>Ghost</Text>
</Button>
);
};
Link
"use client";
import { Button } from "@moe-ui/registry/components/ui/button";
import { Text } from "@moe-ui/registry/components/ui/text";
export const ButtonLinkVariantPreview = () => {
return (
<Button variant="link">
<Text>Link</Text>
</Button>
);
};
Sizes
"use client";
import { Button } from "@moe-ui/registry/components/ui/button";
import { Text } from "@moe-ui/registry/components/ui/text";
import { Plus } from "lucide-react-native";
import { View } from "react-native";
export const ButtonSizesPreview = () => {
return (
<View className="flex-row flex-wrap items-center justify-center gap-3">
<Button size="default">
<Text>Default</Text>
</Button>
<Button size="sm">
<Text>Small</Text>
</Button>
<Button size="lg">
<Text>Large</Text>
</Button>
<Button size="icon" aria-label="Icon button">
<Plus />
</Button>
</View>
);
};
Compound parts and variants remain regular source in your project, so you can change them without wrapping a package API.
Public API
The canonical source panel below lists every public export, dependency, and the complete implementation shipped by the registry.
Keyboard and accessibility
Semantic roles, disabled state, visible focus, and contrast are verified in the component browser matrix.
Browser support
This beta is release-tested in Chromium, Firefox, and WebKit in light and dark themes. See the web compatibility matrix for the current gate.
Nothing hiddenCanonical source and public exports3 exports
ButtonbuttonTextVariantsbuttonVariants"use client";
import { TextClassContext } from "./text";
import { cn } from "../../lib/utils";
import { cva, type VariantProps } from "class-variance-authority";
import { Platform, Pressable } from "react-native";
// NOTE: group-* is not supported yet by Uniwind
const buttonVariants = cva(
cn(
"group shrink-0 flex-row items-center justify-center gap-2 rounded-md shadow-none",
Platform.select({
web: "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive whitespace-nowrap outline-none transition-all focus-visible:ring-[3px] disabled:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
}),
),
{
variants: {
variant: {
default: cn(
"bg-primary active:bg-primary/90 shadow-sm shadow-black/5",
Platform.select({ web: "hover:bg-primary/90" }),
),
destructive: cn(
"bg-destructive active:bg-destructive/90 dark:bg-destructive/60 shadow-sm shadow-black/5",
Platform.select({
web: "hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40",
}),
),
outline: cn(
"border-border bg-background active:bg-accent dark:bg-input/30 dark:border-input dark:active:bg-input/50 border shadow-sm shadow-black/5",
Platform.select({
web: "hover:bg-accent dark:hover:bg-input/50",
}),
),
secondary: cn(
"bg-secondary active:bg-secondary/80 shadow-sm shadow-black/5",
Platform.select({ web: "hover:bg-secondary/80" }),
),
ghost: cn(
"active:bg-accent dark:active:bg-accent/50",
Platform.select({ web: "hover:bg-accent dark:hover:bg-accent/50" }),
),
link: "",
},
size: {
default: cn(
"h-10 px-4 py-2 sm:h-9",
Platform.select({ web: "has-[>svg]:px-3" }),
),
sm: cn(
"h-9 gap-1.5 rounded-md px-3 sm:h-8",
Platform.select({ web: "has-[>svg]:px-2.5" }),
),
lg: cn(
"h-11 rounded-md px-6 sm:h-10",
Platform.select({ web: "has-[>svg]:px-4" }),
),
icon: "h-10 w-10 sm:h-9 sm:w-9",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
},
);
const buttonTextVariants = cva(
cn(
"text-foreground text-sm font-medium",
Platform.select({ web: "pointer-events-none transition-colors" }),
),
{
variants: {
variant: {
default: "text-primary-foreground",
destructive: "text-white",
outline: cn(
"group-active:text-accent-foreground",
Platform.select({ web: "group-hover:text-accent-foreground" }),
),
secondary: "text-secondary-foreground",
ghost: "group-active:text-accent-foreground",
link: cn(
"text-primary group-active:underline",
Platform.select({
web: "underline-offset-4 hover:underline group-hover:underline",
}),
),
},
size: {
default: "",
sm: "",
lg: "",
icon: "",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
},
);
type ButtonProps = React.ComponentProps<typeof Pressable> &
React.RefAttributes<typeof Pressable> &
VariantProps<typeof buttonVariants>;
function Button({ className, variant, size, ...props }: ButtonProps) {
return (
<TextClassContext.Provider value={buttonTextVariants({ variant, size })}>
<Pressable
className={cn(
props.disabled && "opacity-50",
buttonVariants({ variant, size }),
className,
)}
role="button"
{...props}
/>
</TextClassContext.Provider>
);
}
export { Button, buttonTextVariants, buttonVariants };
export type { ButtonProps };