Button
Displays a button or a component that looks like a button.
Preview
<script setup lang="ts">
import { Button } from "~/components/ui/button";
</script>
<template>
<Button>Button</Button>
</template>
Installation
Copy and paste this into your project
// ~/components/ui/button.tsx
import { VariantProps, cva } from "class-variance-authority";
import {
defineComponent,
PropType,
ButtonHTMLAttributes,
ExtractPropTypes,
} from "vue";
import { ExtendProps, cn, renderAsChild } from "~/lib/utils";
export const buttonVariants = cva(
"inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline:
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
icon: "h-10 w-10",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
);
const props = {
variant: {
type: String as PropType<VariantProps<typeof buttonVariants>["variant"]>,
},
size: {
type: String as PropType<VariantProps<typeof buttonVariants>["size"]>,
},
asChild: {
type: Boolean as PropType<boolean | undefined>,
},
};
export type ButtonProps = ExtendProps<
ButtonHTMLAttributes & ExtractPropTypes<typeof props>
>;
const Button = defineComponent({
props: props as unknown as ButtonProps,
setup({ variant, size, asChild }, { slots, attrs }) {
return () =>
asChild ? (
renderAsChild(slots, {
...attrs,
class: cn(buttonVariants({ variant, size }), attrs.class ?? ""),
})
) : (
<button
{...attrs}
class={cn(buttonVariants({ variant, size }), attrs.class ?? "")}
>
{slots.default?.()}
</button>
);
},
});
export { Button };
Usage
import { Button } from "~/components/ui/button";
<Button variant="secondary">Button</Button>
Link
You can use the buttonVariants
helper to create a link that looks like a button.
import { buttonVariants } from "~/components/ui/button"
<Link class="buttonVariants({ variant: 'outline' })">Click here</Link>
Alternatively, you can set the asChild
parameter and nest the link component.
<Button as-child>
<Link href="/login">Login</Link>
</Button>
Examples
Default
<script setup lang="ts">
import { Button } from "~/components/ui/button";
</script>
<template>
<Button>Button</Button>
</template>
Secondary
<script setup lang="ts">
import { Button } from "~/components/ui/button";
</script>
<template>
<Button variant="secondary">Button</Button>
</template>
Destructive
<script setup lang="ts">
import { Button } from "~/components/ui/button";
</script>
<template>
<Button variant="destructive">Button</Button>
</template>
Outline
<script setup lang="ts">
import { Button } from "~/components/ui/button";
</script>
<template>
<Button variant="outline">Button</Button>
</template>
Ghost
<script setup lang="ts">
import { Button } from "~/components/ui/button";
</script>
<template>
<Button variant="ghost">Button</Button>
</template>
Link
<script setup lang="ts">
import { Button } from "~/components/ui/button";
</script>
<template>
<Button variant="link">Button</Button>
</template>
Icon
<script setup lang="ts">
import { Button } from "~/components/ui/button";
import { Mail } from "lucide-vue-next";
</script>
<template>
<Button variant="outline" size="icon">
<Mail class="w-4 h-4" />
</Button>
</template>
With Icon
<script setup lang="ts">
import { Button } from "~/components/ui/button";
import { Mail } from "lucide-vue-next";
</script>
<template>
<Button> <Mail class="mr-2 h-4 w-4" /> Login with Email </Button>
</template>
Loading
<script setup lang="ts">
import { Button } from "~/components/ui/button";
import { Loader2 } from "lucide-vue-next";
</script>
<template>
<Button disabled>
<Loader2 class="mr-2 h-4 w-4 animate-spin" />
Please wait
</Button>
</template>