# Get Started
---
title: Get Started
description: Install and configure fab-ui components in your project.
---
## Installation
### Direct URL
Install components directly without any configuration:
```bash
npx shadcn@latest add https://fab-ui.com/r/button.json
```
### Namespaced Registry
For a cleaner workflow, configure the fab-ui registry in your `components.json`:
```json title="components.json"
{
"$schema": "https://shadcn.com/schema.json",
"registries": {
"fab-ui": "https://fab-ui.com/r/{name}.json"
}
}
```
Then install components using short names:
```bash
npx shadcn@latest add fab-ui/button
```
## Portals Setup
fab-ui uses portals for popup components like Dialog and Popover. To ensure these display above all page content, add an `isolation: isolate` style to your application root element:
```css title="globals.css"
.root {
isolation: isolate;
}
```
This creates a separate stacking context, preventing `z-index` conflicts with other page styles.
## iOS 26+ Safari
Starting with iOS 26, Safari displays content beneath the UI chrome. Dialog backdrops and similar elements must use `position: absolute` instead of `position: fixed` to cover the visual viewport properly after scrolling. Add this style globally:
```css title="globals.css"
body {
position: relative;
}
```
## Working with LLMs
fab-ui is built on Base UI. When working with AI assistants, you can reference the Base UI documentation in markdown format for accurate component APIs and patterns.
See [Base UI's llms.txt](https://base-ui.com/llms.txt) for AI-friendly documentation.
# Introduction
---
title: Introduction
description: A modern registry of React and Next.js components, built on top of Base UI and beyond. Open source, accessible, customizable.
---
fab-ui provides a collection of production-ready components that you own completely. Install them directly into your project, customize them to your needs, and ship with confidence.
## Why fab-ui?
| Feature | Description |
| ------------------------- | ------------------------------------------------------------------- |
| **Built on Base UI** | Leverages Base UI's headless, accessible primitives as a foundation |
| **Accessible by Default** | ARIA-compliant components with full keyboard navigation |
| **CLI Installation** | Install directly with shadcn CLI - components live in your codebase |
| **Full Ownership** | No runtime dependency - copy, customize, and extend freely |
| **TailwindCSS Styling** | Modern utility-first styling with full customization |
| **TypeScript Support** | Fully typed components for better developer experience |
## Philosophy
- **Ownership over dependency** - Components live in your codebase, not node_modules
- **Accessible first** - Built on proven accessibility patterns from Base UI
- **Minimal footprint** - No runtime overhead, just the code you need
- **Customization freedom** - Modify anything without fighting the library
# Styling
---
title: Styling
description: Configure colors and theming for fab-ui components.
---
fab-ui components use CSS variables for theming. Adding these variables is optional, but provides consistent styling out of the box.
## CSS Variables
For the default color scheme, add these shadcn Tailwind CSS v4 variables to your `globals.css`:
```css title="app/globals.css" showLineNumbers
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: var(--font-sans);
--font-mono: var(--font-geist-mono);
--color-sidebar-ring: var(--sidebar-ring);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar: var(--sidebar);
--color-chart-5: var(--chart-5);
--color-chart-4: var(--chart-4);
--color-chart-3: var(--chart-3);
--color-chart-2: var(--chart-2);
--color-chart-1: var(--chart-1);
--color-ring: var(--ring);
--color-input: var(--input);
--color-border: var(--border);
--color-destructive: var(--destructive);
--color-accent-foreground: var(--accent-foreground);
--color-accent: var(--accent);
--color-muted-foreground: var(--muted-foreground);
--color-muted: var(--muted);
--color-secondary-foreground: var(--secondary-foreground);
--color-secondary: var(--secondary);
--color-primary-foreground: var(--primary-foreground);
--color-primary: var(--primary);
--color-popover-foreground: var(--popover-foreground);
--color-popover: var(--popover);
--color-card-foreground: var(--card-foreground);
--color-card: var(--card);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--radius-2xl: calc(var(--radius) + 8px);
--radius-3xl: calc(var(--radius) + 12px);
--radius-4xl: calc(var(--radius) + 16px);
}
:root {
--background: oklch(1 0 0);
--foreground: oklch(0.141 0.005 285.823);
--card: oklch(1 0 0);
--card-foreground: oklch(0.141 0.005 285.823);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.141 0.005 285.823);
--primary: oklch(0.21 0.006 285.885);
--primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.967 0.001 286.375);
--secondary-foreground: oklch(0.21 0.006 285.885);
--muted: oklch(0.967 0.001 286.375);
--muted-foreground: oklch(0.552 0.016 285.938);
--accent: oklch(0.967 0.001 286.375);
--accent-foreground: oklch(0.21 0.006 285.885);
--destructive: oklch(0.577 0.245 27.325);
--border: oklch(0.92 0.004 286.32);
--input: oklch(0.92 0.004 286.32);
--ring: oklch(0.705 0.015 286.067);
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.398 0.07 227.392);
--chart-4: oklch(0.828 0.189 84.429);
--chart-5: oklch(0.769 0.188 70.08);
--radius: 0.625rem;
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.141 0.005 285.823);
--sidebar-primary: oklch(0.21 0.006 285.885);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.967 0.001 286.375);
--sidebar-accent-foreground: oklch(0.21 0.006 285.885);
--sidebar-border: oklch(0.92 0.004 286.32);
--sidebar-ring: oklch(0.705 0.015 286.067);
}
.dark {
--background: oklch(0.141 0.005 285.823);
--foreground: oklch(0.985 0 0);
--card: oklch(0.21 0.006 285.885);
--card-foreground: oklch(0.985 0 0);
--popover: oklch(0.21 0.006 285.885);
--popover-foreground: oklch(0.985 0 0);
--primary: oklch(0.92 0.004 286.32);
--primary-foreground: oklch(0.21 0.006 285.885);
--secondary: oklch(0.274 0.006 286.033);
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.274 0.006 286.033);
--muted-foreground: oklch(0.705 0.015 286.067);
--accent: oklch(0.274 0.006 286.033);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.704 0.191 22.216);
--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 15%);
--ring: oklch(0.552 0.016 285.938);
--chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48);
--chart-3: oklch(0.769 0.188 70.08);
--chart-4: oklch(0.627 0.265 303.9);
--chart-5: oklch(0.645 0.246 16.439);
--sidebar: oklch(0.21 0.006 285.885);
--sidebar-foreground: oklch(0.985 0 0);
--sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.274 0.006 286.033);
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(1 0 0 / 10%);
--sidebar-ring: oklch(0.552 0.016 285.938);
}
```
## What's Included
| Category | Variables |
| --------------- | ---------------------------------------------------------------------- |
| **Base colors** | background, foreground, primary, secondary, muted, accent, destructive |
| **Components** | card, popover, sidebar (with foreground variants) |
| **Charts** | chart-1 through chart-5 |
| **Utilities** | border, input, ring, radius |
## Dark Mode
Toggle dark mode by adding the `.dark` class to your root element. The variables automatically switch to dark mode values.
# Accordion
---
title: Accordion
description: A set of collapsible panels with headings.
links:
doc: https://base-ui.com/react/components/accordion
api: https://base-ui.com/react/components/accordion#api-reference
---
```tsx
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from '@/components/ui/accordion';
type AccordionItem = {
id: string;
title: string;
content: string;
};
export const accordionItems: AccordionItem[] = [
{
id: 'item-1',
title: 'What is your return policy?',
content:
"We accept returns within 30 days of purchase, provided the item is unused and in its original condition. To start a return, you'll need your order number and the email address used at checkout. Once your return is approved, we'll send you instructions along with a prepaid return label (where applicable). Refunds are typically processed within 5-7 business days after the item is received.",
},
{
id: 'item-2',
title: 'How long does shipping take?',
content:
"Shipping times depend on your location and the shipping method selected at checkout. Standard shipping usually takes 3-5 business days, while express shipping can arrive in 1-2 business days. You'll receive a tracking link as soon as your order ships, so you can monitor delivery progress. Please note that delays may occur during peak periods or due to customs processing for international orders.",
},
{
id: 'item-3',
title: 'Do you offer customer support?',
content:
'Yes, our customer support team is here to help. You can reach us Monday through Friday from 9am to 5pm via email or live chat. We aim to respond to all inquiries within 24 hours, and often much sooner. For faster service, include your order number and a brief description of the issue so we can resolve it as quickly as possible.',
},
];
export function AccordionDemo() {
return (
{accordionItems.map((accordionItem) => {
return (
{accordionItem.title}{accordionItem.content}
);
})}
);
}
```
## Installation
```bash
npx shadcn@latest add @fab-ui/accordion
```
**Install the following dependencies:**
```bash
npm install @base-ui/react
```
**Copy and paste the following code into your project.**
```tsx
'use client';
import { Accordion as AccordionPrimitive } from '@base-ui/react/accordion';
import { PlusIcon } from 'lucide-react';
import { cn } from '@/lib/utils';
function Accordion({ className, ...props }: AccordionPrimitive.Root.Props) {
return (
);
}
function AccordionItem({ className, ...props }: AccordionPrimitive.Item.Props) {
return (
);
}
function AccordionTrigger({
className,
children,
...props
}: AccordionPrimitive.Trigger.Props) {
return (
svg]:rotate-45',
className
)}
data-slot='accordion-trigger'
{...props}
>
{children}
);
}
function AccordionContent({
className,
children,
...props
}: AccordionPrimitive.Panel.Props) {
return (
{children}
);
}
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
```
**Update the import paths to match your project setup.**
## Usage
```tsx
import {
Accordion,
AccordionItem,
AccordionPanel,
AccordionTrigger
} from "@/components/ui/accordion"
```
```tsx
Is it accessible?
Yes. It adheres to the WAI-ARIA design pattern.
```
# Alert Dialog
---
title: Alert Dialog
description: A dialog that requires a user response to proceed.
links:
doc: https://base-ui.com/react/components/alert-dialog
api: https://base-ui.com/react/components/alert-dialog#api-reference
---
```tsx
'use client';
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from '@/components/ui/alert-dialog';
import { Button } from '@/components/ui/button';
export function AlertDialogDemo() {
return (
Show Dialog}
/>
Are you sure?
This action cannot be undone. This will permanently delete your data
from our servers.
CancelContinue
);
}
```
## Installation
```bash
npx shadcn@latest add @fab-ui/alert-dialog
```
**Install the following dependencies:**
```bash
npm install @base-ui/react
```
**Copy and paste the following code into your project.**
```tsx
'use client';
import * as React from 'react';
import { Button } from '@/components/ui/button';
import { AlertDialog as AlertDialogPrimitive } from '@base-ui/react/alert-dialog';
import { cn } from '@/lib/utils';
function AlertDialog({ ...props }: AlertDialogPrimitive.Root.Props) {
return ;
}
function AlertDialogTrigger({ ...props }: AlertDialogPrimitive.Trigger.Props) {
return (
);
}
function AlertDialogPortal({ ...props }: AlertDialogPrimitive.Portal.Props) {
return (
);
}
function AlertDialogOverlay({
className,
...props
}: AlertDialogPrimitive.Backdrop.Props) {
return (
);
}
function AlertDialogContent({
className,
size = 'default',
...props
}: AlertDialogPrimitive.Popup.Props & {
size?: 'default' | 'sm';
}) {
return (
);
}
function AlertDialogHeader({
className,
...props
}: React.ComponentProps<'div'>) {
return (
);
}
function AlertDialogFooter({
className,
...props
}: React.ComponentProps<'div'>) {
return (
);
}
function AlertDialogMedia({
className,
...props
}: React.ComponentProps<'div'>) {
return (
);
}
function AlertDialogTitle({
className,
...props
}: React.ComponentProps) {
return (
);
}
function AlertDialogDescription({
className,
...props
}: React.ComponentProps) {
return (
);
}
function AlertDialogAction({
className,
...props
}: React.ComponentProps) {
return (
);
}
function AlertDialogCancel({
className,
variant = 'outline',
size = 'default',
...props
}: AlertDialogPrimitive.Close.Props &
Pick, 'variant' | 'size'>) {
return (
}
{...props}
/>
);
}
export {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogMedia,
AlertDialogOverlay,
AlertDialogPortal,
AlertDialogTitle,
AlertDialogTrigger,
};
```
**Update the import paths to match your project setup.**
## Usage
```tsx showLineNumbers
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog"
```
```tsx showLineNumbers
OpenAre you sure?
This action cannot be undone. This will permanently delete your data from our servers.
CancelContinue
```
# Autocomplete
---
title: Autocomplete
description: An input that suggests options as you type.
links:
doc: https://base-ui.com/react/components/autocomplete
api: https://base-ui.com/react/components/autocomplete#api-reference
---
```tsx
'use client';
import {
Autocomplete,
AutocompleteEmpty,
AutocompleteInput,
AutocompleteItem,
AutocompleteList,
AutocompletePopup,
} from '@/components/ui/autocomplete';
const items = [
{ label: 'React', value: 'react' },
{ label: 'Next.js', value: 'nextjs' },
{ label: 'Vue.js', value: 'vue' },
{ label: 'Angular', value: 'angular' },
{ label: 'Svelte', value: 'svelte' },
{ label: 'TypeScript', value: 'typescript' },
{ label: 'Express', value: 'express' },
{ label: 'Tailwind CSS', value: 'tailwindcss' },
];
export function AutocompleteDemo() {
return (
No items found.
{(item) => (
{item.label}
)}
);
}
```
## Installation
```bash
npx shadcn@latest add @fab-ui/autocomplete
```
**Install the following dependencies:**
```bash
npm install @base-ui/react
```
**Copy and paste the following code into your project.**
```tsx
'use client';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Autocomplete as AutocompletePrimitive } from '@base-ui/react/autocomplete';
import { XIcon } from 'lucide-react';
import { cn } from '@/lib/utils';
const Autocomplete = AutocompletePrimitive.Root;
function AutocompleteInput(props: AutocompletePrimitive.Input.Props) {
return (
}
{...props}
/>
);
}
function AutocompletePopup({
className,
sideOffset = 4,
children,
...props
}: AutocompletePrimitive.Popup.Props & {
sideOffset?: number;
}) {
return (
{children}
);
}
function AutocompletePositioner({
className,
...props
}: AutocompletePrimitive.Positioner.Props) {
return (
);
}
function AutocompleteList({
className,
...props
}: AutocompletePrimitive.List.Props) {
return (
);
}
function AutocompleteEmpty({
className,
...props
}: AutocompletePrimitive.Empty.Props) {
return (
);
}
function AutocompleteItem({
className,
...props
}: AutocompletePrimitive.Item.Props) {
return (
);
}
function AutocompleteGroup({
className,
...props
}: AutocompletePrimitive.Group.Props) {
return (
);
}
function AutocompleteGroupLabel({
className,
...props
}: AutocompletePrimitive.GroupLabel.Props) {
return (
);
}
function AutocompleteCollection({
...props
}: AutocompletePrimitive.Collection.Props) {
return (
);
}
function AutocompleteStatus({
className,
...props
}: AutocompletePrimitive.Status.Props) {
return (
);
}
function AutocompleteClear({
className,
children,
...props
}: AutocompletePrimitive.Clear.Props) {
return (
{children ?? }
);
}
function AutocompleteRow({
className,
...props
}: AutocompletePrimitive.Row.Props) {
return (
);
}
function AutocompleteTrigger({
className,
...props
}: AutocompletePrimitive.Trigger.Props) {
return (
}
{...props}
/>
);
}
export {
Autocomplete,
AutocompleteClear,
AutocompleteCollection,
AutocompleteEmpty,
AutocompleteGroup,
AutocompleteGroupLabel,
AutocompleteInput,
AutocompleteItem,
AutocompleteList,
AutocompletePopup,
AutocompletePositioner,
AutocompleteRow,
AutocompleteStatus,
AutocompleteTrigger,
};
```
**Update the import paths to match your project setup.**
## Usage
```tsx showLineNumbers
import {
Autocomplete,
AutocompleteEmpty,
AutocompleteInput,
AutocompleteItem,
AutocompleteList,
AutocompletePopup,
} from "@/components/ui/autocomplete"
```
```tsx showLineNumbers
const items = [
{ label: 'React', value: 'react' },
{ label: 'Next.js', value: 'nextjs' },
{ label: 'Vue.js', value: 'vue' },
{ label: 'Angular', value: 'angular' },
{ label: 'Svelte', value: 'svelte' },
];
No results found.
{(item) => {item.label}}
```
# Avatar
---
title: Avatar
description: An easily stylable avatar component.
links:
doc: https://base-ui.com/react/components/avatar
api: https://base-ui.com/react/components/avatar#api-reference
---
```tsx
import {
Avatar,
AvatarFallback,
AvatarImage,
} from '@/components/ui/avatar';
export function AvatarDemo() {
return (
AI
);
}
```
## Installation
```bash
npx shadcn@latest add @fab-ui/avatar
```
**Install the following dependencies:**
```bash
npm install @base-ui/react
```
**Copy and paste the following code into your project.**
```tsx
import { Avatar as AvatarPrimitive } from '@base-ui/react/avatar';
import { cn } from '@/lib/utils';
function Avatar({ className, ...props }: AvatarPrimitive.Root.Props) {
return (
);
}
function AvatarImage({ className, ...props }: AvatarPrimitive.Image.Props) {
return (
);
}
function AvatarFallback({
className,
...props
}: AvatarPrimitive.Fallback.Props) {
return (
);
}
function AvatarBadge({ className, ...props }: React.ComponentProps<'span'>) {
return (
svg]:size-3',
className
)}
{...props}
/>
);
}
function AvatarGroup({ className, ...props }: React.ComponentProps<'div'>) {
return (
);
}
function AvatarGroupCount({
className,
...props
}: React.ComponentProps<'div'>) {
return (
svg]:size-3',
className
)}
{...props}
/>
);
}
export {
Avatar,
AvatarImage,
AvatarFallback,
AvatarBadge,
AvatarGroup,
AvatarGroupCount,
};
```
**Update the import paths to match your project setup.**
## Usage
```tsx
import {
Avatar,
AvatarFallback,
AvatarImage
} from "@/components/ui/avatar"
```
```tsx
CN
```
## Examples
### With Badge
```tsx
import {
Avatar,
AvatarBadge,
AvatarFallback,
AvatarImage,
} from '@/components/ui/avatar';
export function AvatarWithBadge() {
return (
JD
);
}
```
### Badge with Icon
```tsx
import {
Avatar,
AvatarBadge,
AvatarFallback,
AvatarImage,
} from '@/components/ui/avatar';
import { PlusIcon } from 'lucide-react';
export function AvatarBadgeIcon() {
return (
MK
);
}
```
### Group
```tsx
import {
Avatar,
AvatarFallback,
AvatarGroup,
AvatarGroupCount,
AvatarImage,
} from '@/components/ui/avatar';
export function AvatarGroupDemo() {
return (
JDMKTS+3
);
}
```
# Badge
---
title: Badge
description: Displays a badge or a component that looks like a badge.
---
```tsx
import { Badge } from '@/components/ui/badge';
import { BadgeCheckIcon } from 'lucide-react';
export function BadgeDemo() {
return (
BadgeSecondaryDestructiveOutline
Verified
8
99
20+
);
}
```
## Installation
```bash
npx shadcn@latest add @fab-ui/badge
```
**Install the following dependencies:**
```bash
npm install @base-ui/react
```
**Copy and paste the following code into your project.**
```tsx
import { mergeProps } from '@base-ui/react/merge-props';
import { useRender } from '@base-ui/react/use-render';
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@/lib/utils';
const badgeVariants = cva(
'h-5 gap-1 rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium transition-all has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&>svg]:size-3! inline-flex items-center justify-center w-fit whitespace-nowrap shrink-0 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-colors overflow-hidden group/badge',
{
variants: {
variant: {
default: 'bg-primary text-primary-foreground [a]:hover:bg-primary/80',
secondary:
'bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80',
destructive:
'bg-destructive/20 [a]:hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 text-destructive dark:bg-destructive/20',
outline:
'border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground bg-background dark:bg-input/30 dark:border-input',
ghost:
'hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50',
link: 'text-primary underline-offset-4 hover:underline',
},
},
defaultVariants: {
variant: 'default',
},
}
);
function Badge({
className,
variant = 'default',
render,
...props
}: useRender.ComponentProps<'span'> & VariantProps) {
return useRender({
defaultTagName: 'span',
props: mergeProps<'span'>(
{
className: cn(badgeVariants({ className, variant })),
},
props
),
render,
state: {
slot: 'badge',
variant,
},
});
}
export { Badge, badgeVariants };
```
**Update the import paths to match your project setup.**
## Usage
```tsx
import { Badge } from "@/components/ui/badge"
```
```tsx
Badge
```
### Link
You can use the `render` prop to make another component look like a badge. Here's an example of a link that looks like a badge.
```tsx showLineNumbers
import Link from "next/link"
import { Badge } from "@/components/ui/badge"
export function BadgeLink() {
return }>Badge;
}
```
# Button
---
title: Button
description: A button component that can be rendered as another tag or focusable when disabled.
links:
doc: https://base-ui.com/react/components/button
api: https://base-ui.com/react/components/button#api-reference
---
```tsx
import { Button } from '@/components/ui/button';
export function ButtonDefault() {
return ;
}
```
## Installation
```bash
npx shadcn@latest add @fab-ui/button
```
**Install the following dependencies:**
```bash
npm install @base-ui/react
```
**Copy and paste the following code into your project.**
```tsx
'use client';
import { Button as ButtonPrimitive } from '@base-ui/react/button';
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@/lib/utils';
const buttonVariants = cva(
"focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-4xl border border-transparent bg-clip-padding text-sm font-medium focus-visible:ring-[3px] aria-invalid:ring-[3px] [&_svg:not([class*='size-'])]:size-4 inline-flex items-center justify-center whitespace-nowrap transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none shrink-0 [&_svg]:shrink-0 outline-none group/button select-none",
{
variants: {
variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/80',
outline:
'border-border bg-background hover:bg-accent hover:text-accent-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50',
secondary:
'bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground',
ghost:
'hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground',
destructive:
'bg-destructive/10 hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/20 text-destructive focus-visible:border-destructive/40 dark:hover:bg-destructive/30',
link: 'text-primary underline-offset-4 hover:underline',
},
size: {
default:
'h-9 gap-1.5 px-3 has-data-[icon=inline-end]:pr-2.5 has-data-[icon=inline-start]:pl-2.5',
xs: "h-6 gap-1 px-2.5 text-xs has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-3",
sm: 'h-8 gap-1 px-3 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2',
lg: 'h-10 gap-1.5 px-4 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3',
icon: 'size-9',
'icon-xs': "size-6 [&_svg:not([class*='size-'])]:size-3",
'icon-sm': 'size-8',
'icon-lg': 'size-10',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
}
);
function Button({
className,
variant = 'default',
size = 'default',
...props
}: ButtonPrimitive.Props & VariantProps) {
return (
);
}
export { Button, buttonVariants };
```
**Update the import paths to match your project setup.**
## Usage
```tsx
import { Button } from "@/components/ui/button"
```
```tsx
```
## Cursor
Tailwind v4 [switched](https://tailwindcss.com/docs/upgrade-guide#buttons-use-the-default-cursor) from `cursor: pointer` to `cursor: default` for the button component.
If you want to keep the `cursor: pointer` behavior, add the following code to your CSS file:
```css showLineNumbers title="globals.css"
@layer base {
button:not(:disabled),
[role='button']:not(:disabled) {
cursor: pointer;
}
}
```
## Examples
### Outline
```tsx
import { Button } from '@/components/ui/button';
export function ButtonOutline() {
return ;
}
```
```tsx
```
### Secondary
```tsx
import { Button } from '@/components/ui/button';
export function ButtonSecondary() {
return ;
}
```
```tsx
```
### Ghost
```tsx
import { Button } from '@/components/ui/button';
export function ButtonGhost() {
return ;
}
```
```tsx
```
### Link
```tsx
import { Button } from '@/components/ui/button';
export function ButtonLink() {
return ;
}
```
```tsx
```
### Icon
```tsx
import { Button } from '@/components/ui/button';
import { ChevronRightIcon } from 'lucide-react';
export function ButtonIcon() {
return (
);
}
```
```tsx
```
### With Icon
The spacing between the icon and the text is automatically adjusted
based on the size of the button. You do not need any margin on the icon.
```tsx
import { Button } from '@/components/ui/button';
import { ArrowRightIcon } from 'lucide-react';
export function ButtonWithIcon() {
return (
);
}
```
```tsx
```
## API Reference
### Button
| Prop | Type | Default |
| --------- | ----------------------------------------------------------------------------- | ----------- |
| `variant` | `"default" \| "outline" \| "ghost" \| "destructive" \| "secondary" \| "link"` | `"default"` |
| `size` | `"default" \| "sm" \| "lg" \| "icon" \| "icon-sm" \| "icon-lg"` | `"default"` |
# Card
---
title: Card
description: Displays a card with header, content, and footer.
---
```tsx
'use client';
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogMedia,
AlertDialogTitle,
AlertDialogTrigger,
} from '@/components/ui/alert-dialog';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import {
Card,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from '@/components/ui/card';
import { BluetoothIcon, PlusIcon } from 'lucide-react';
export function CardDemo() {
return (
Observability Plus is replacing Monitoring
Switch to the improved way to explore your data, with natural
language. Monitoring will no longer be available on the Pro plan in
November, 2025
}>
Show Dialog
Allow accessory to connect?
Do you want to allow the USB accessory to connect to this
device?
Don't allowAllow
Warning
);
}
```
## Installation
```bash
npx shadcn@latest add @fab-ui/card
```
**Install the following dependencies:**
```bash
npm install @base-ui/react
```
**Copy and paste the following code into your project.**
```tsx
import * as React from 'react';
import { cn } from '@/lib/utils';
function Card({
className,
size = 'default',
...props
}: React.ComponentProps<'div'> & { size?: 'default' | 'sm' }) {
return (
```
# Checkbox Group
---
title: Checkbox Group
description: A group of checkboxes that can be controlled together.
links:
doc: https://base-ui.com/react/components/checkbox-group
api: https://base-ui.com/react/components/checkbox-group#api-reference
---
```tsx
import { Checkbox } from '@/components/ui/checkbox';
import { CheckboxGroup } from '@/components/ui/checkbox-group';
import { Label } from '@/components/ui/label';
export function CheckboxGroupDefault() {
return (
);
}
```
## Installation
```bash
npx shadcn@latest add @fab-ui/checkbox-group
```
**Install the following dependencies:**
```bash
npm install @base-ui/react
```
**Copy and paste the following code into your project.**
```tsx
import { CheckboxGroup as CheckboxGroupPrimitive } from '@base-ui/react/checkbox-group';
import { cn } from '@/lib/utils';
function CheckboxGroup({ className, ...props }: CheckboxGroupPrimitive.Props) {
return (
);
}
export { CheckboxGroup };
```
**Update the import paths to match your project setup.**
## Usage
```tsx
import { Checkbox } from "@/components/ui/checkbox"
import { CheckboxGroup } from "@/components/ui/checkbox-group"
import { Label } from "@/components/ui/label"
```
```tsx
```
## Examples
### With Field
```tsx
import { Checkbox } from '@/components/ui/checkbox';
import { CheckboxGroup } from '@/components/ui/checkbox-group';
import {
Field,
FieldDescription,
FieldLabel,
} from '@/components/ui/field';
import { Label } from '@/components/ui/label';
export function CheckboxGroupWithField() {
return (
Email Preferences
Choose which emails you'd like to receive.
);
}
```
# Checkbox
---
title: Checkbox
description: A control that allows the user to toggle between checked and not checked.
links:
doc: https://base-ui.com/react/components/checkbox
api: https://base-ui.com/react/components/checkbox#api-reference
---
```tsx
import { Checkbox } from '@/components/ui/checkbox';
export function CheckboxDefault() {
return ;
}
```
## Installation
```bash
npx shadcn@latest add @fab-ui/checkbox
```
**Install the following dependencies:**
```bash
npm install @base-ui/react
```
**Copy and paste the following code into your project.**
```tsx
'use client';
import { Checkbox as CheckboxPrimitive } from '@base-ui/react/checkbox';
import { CheckIcon } from 'lucide-react';
import { cn } from '@/lib/utils';
function Checkbox({ className, ...props }: CheckboxPrimitive.Root.Props) {
return (
);
}
export { Checkbox };
```
**Update the import paths to match your project setup.**
## Usage
```tsx
import { Checkbox } from "@/components/ui/checkbox"
```
```tsx
```
## Examples
### Checked
```tsx
import { Checkbox } from '@/components/ui/checkbox';
export function CheckboxChecked() {
return ;
}
```
### Disabled
```tsx
import { Checkbox } from '@/components/ui/checkbox';
export function CheckboxDisabled() {
return (
);
}
```
### With Label
```tsx
import { Checkbox } from '@/components/ui/checkbox';
import { Label } from '@/components/ui/label';
export function CheckboxWithLabel() {
return (
);
}
```
# Collapsible
---
title: Collapsible
description: An interactive component that expands and collapses content.
links:
doc: https://base-ui.com/react/components/collapsible
api: https://base-ui.com/react/components/collapsible#api-reference
---
```tsx
'use client';
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from '@/components/ui/collapsible';
import { ChevronDownIcon } from 'lucide-react';
export function CollapsibleDefault() {
return (
Show more
The chevron icon rotates when the panel is open.
);
}
```
## Installation
```bash
npx shadcn@latest add @fab-ui/collapsible
```
**Install the following dependencies:**
```bash
npm install @base-ui/react
```
**Copy and paste the following code into your project.**
```tsx
'use client';
import { Collapsible as CollapsiblePrimitive } from '@base-ui/react/collapsible';
function Collapsible({ ...props }: CollapsiblePrimitive.Root.Props) {
return ;
}
function CollapsibleTrigger({ ...props }: CollapsiblePrimitive.Trigger.Props) {
return (
);
}
function CollapsibleContent({ ...props }: CollapsiblePrimitive.Panel.Props) {
return (
);
}
export { Collapsible, CollapsibleTrigger, CollapsibleContent };
```
**Update the import paths to match your project setup.**
## Usage
```tsx
import {
Collapsible,
CollapsiblePanel,
CollapsibleTrigger,
} from "@/components/ui/collapsible"
```
```tsx
Toggle
Content that can be shown or hidden.
```
# Combobox
---
title: Combobox
description: A searchable dropdown that allows users to filter and select from a list of options.
links:
doc: https://base-ui.com/react/components/combobox
api: https://base-ui.com/react/components/combobox#api-reference
---
```tsx
'use client';
import {
Combobox,
ComboboxContent,
ComboboxEmpty,
ComboboxInput,
ComboboxItem,
ComboboxList,
} from '@/components/ui/combobox';
const frameworks = ['React', 'Vue', 'Angular', 'Svelte', 'Next.js', 'Nuxt'];
export function ComboboxDefault() {
return (
No frameworks found.
{(item) => (
{item}
)}
);
}
```
## Installation
```bash
npx shadcn@latest add @fab-ui/combobox
```
**Install the following dependencies:**
```bash
npm install @base-ui/react
```
**Copy and paste the following code into your project.**
```tsx
'use client';
import * as React from 'react';
import { Button } from '@/components/ui/button';
import {
InputGroup,
InputGroupAddon,
InputGroupButton,
InputGroupInput,
} from '@/components/ui/input-group';
import { Combobox as ComboboxPrimitive } from '@base-ui/react/combobox';
import { CheckIcon, ChevronDownIcon, XIcon } from 'lucide-react';
import { cn } from '@/lib/utils';
const Combobox = ComboboxPrimitive.Root;
function ComboboxValue({ ...props }: ComboboxPrimitive.Value.Props) {
return ;
}
function ComboboxTrigger({
className,
children,
...props
}: ComboboxPrimitive.Trigger.Props) {
return (
{children}
);
}
function ComboboxClear({ className, ...props }: ComboboxPrimitive.Clear.Props) {
return (
}
/>
);
}
function ComboboxInput({
className,
children,
disabled = false,
showTrigger = true,
showClear = false,
...props
}: ComboboxPrimitive.Input.Props & {
showTrigger?: boolean;
showClear?: boolean;
}) {
return (
}
{...props}
/>
{showTrigger && (
}
data-slot='input-group-button'
className='group-has-data-[slot=combobox-clear]/input-group:hidden data-pressed:bg-transparent'
disabled={disabled}
/>
)}
{showClear && }
{children}
);
}
function ComboboxContent({
className,
side = 'bottom',
sideOffset = 6,
align = 'start',
alignOffset = 0,
anchor,
...props
}: ComboboxPrimitive.Popup.Props &
Pick<
ComboboxPrimitive.Positioner.Props,
'side' | 'align' | 'sideOffset' | 'alignOffset' | 'anchor'
>) {
return (
);
}
function ComboboxList({ className, ...props }: ComboboxPrimitive.List.Props) {
return (
);
}
function ComboboxItem({
className,
children,
...props
}: ComboboxPrimitive.Item.Props) {
return (
{children}
}
/>
);
}
function ComboboxGroup({ className, ...props }: ComboboxPrimitive.Group.Props) {
return (
);
}
function ComboboxLabel({
className,
...props
}: ComboboxPrimitive.GroupLabel.Props) {
return (
);
}
function ComboboxCollection({ ...props }: ComboboxPrimitive.Collection.Props) {
return (
);
}
function ComboboxEmpty({ className, ...props }: ComboboxPrimitive.Empty.Props) {
return (
);
}
function ComboboxSeparator({
className,
...props
}: ComboboxPrimitive.Separator.Props) {
return (
);
}
function ComboboxChips({
className,
...props
}: React.ComponentPropsWithRef &
ComboboxPrimitive.Chips.Props) {
return (
);
}
function ComboboxChip({
className,
children,
showRemove = true,
...props
}: ComboboxPrimitive.Chip.Props & {
showRemove?: boolean;
}) {
return (
{children}
{showRemove && (
}
/>
)}
);
}
function ComboboxChipsInput({
className,
...props
}: ComboboxPrimitive.Input.Props) {
return (
);
}
function useComboboxAnchor() {
return React.useRef(null);
}
export {
Combobox,
ComboboxInput,
ComboboxContent,
ComboboxList,
ComboboxItem,
ComboboxGroup,
ComboboxLabel,
ComboboxCollection,
ComboboxEmpty,
ComboboxSeparator,
ComboboxChips,
ComboboxChip,
ComboboxChipsInput,
ComboboxTrigger,
ComboboxValue,
useComboboxAnchor,
};
```
**Update the import paths to match your project setup.**
## Usage
```tsx
import {
Combobox,
ComboboxContent,
ComboboxEmpty,
ComboboxInput,
ComboboxItem,
ComboboxList,
} from "@/components/ui/combobox"
```
```tsx
const items = ['Apple', 'Banana', 'Orange', 'Grape'];
No fruits found.
{(item) => (
{item}
)}
```
## Examples
### With Clear Button
```tsx
'use client';
import {
Combobox,
ComboboxContent,
ComboboxEmpty,
ComboboxInput,
ComboboxItem,
ComboboxList,
} from '@/components/ui/combobox';
const countries = [
'United States',
'United Kingdom',
'Canada',
'Australia',
'Germany',
'France',
];
export function ComboboxWithClear() {
return (
No countries found.
{(item) => (
{item}
)}
);
}
```
### Multiple Selection
```tsx
'use client';
import * as React from 'react';
import {
Combobox,
ComboboxChip,
ComboboxChips,
ComboboxChipsInput,
ComboboxContent,
ComboboxEmpty,
ComboboxItem,
ComboboxList,
ComboboxValue,
useComboboxAnchor,
} from '@/components/ui/combobox';
const frameworks = [
'Next.js',
'SvelteKit',
'Nuxt.js',
'Remix',
'Astro',
] as const;
export function ComboboxMultiple() {
const anchor = useComboboxAnchor();
return (
{(values) => (
{values.map((value: string) => (
{value}
))}
)}
No items found.
{(item) => (
{item}
)}
);
}
```
### Grouped Items
```tsx
'use client';
import {
Combobox,
ComboboxCollection,
ComboboxContent,
ComboboxEmpty,
ComboboxGroup,
ComboboxInput,
ComboboxItem,
ComboboxLabel,
ComboboxList,
ComboboxSeparator,
} from '@/components/ui/combobox';
const timezones = [
{
value: 'Americas',
items: [
'(GMT-5) New York',
'(GMT-8) Los Angeles',
'(GMT-6) Chicago',
'(GMT-5) Toronto',
'(GMT-8) Vancouver',
'(GMT-3) São Paulo',
],
},
{
value: 'Europe',
items: [
'(GMT+0) London',
'(GMT+1) Paris',
'(GMT+1) Berlin',
'(GMT+1) Rome',
'(GMT+1) Madrid',
'(GMT+1) Amsterdam',
],
},
{
value: 'Asia/Pacific',
items: [
'(GMT+9) Tokyo',
'(GMT+8) Shanghai',
'(GMT+8) Singapore',
'(GMT+4) Dubai',
'(GMT+11) Sydney',
'(GMT+9) Seoul',
],
},
] as const;
export function ComboboxWithGroupsAndSeparator() {
return (
No timezones found.
{(group, index) => (
{group.value}
{(item) => (
{item}
)}
{index < timezones.length - 1 && }
)}
);
}
```
### Invalid State
```tsx
'use client';
import {
Combobox,
ComboboxContent,
ComboboxEmpty,
ComboboxInput,
ComboboxItem,
ComboboxList,
} from '@/components/ui/combobox';
const frameworks = ['React', 'Vue', 'Angular', 'Svelte', 'Next.js', 'Nuxt'];
export function ComboboxInvalid() {
return (
No frameworks found.
{(item) => (
{item}
)}
);
}
```
### Disabled
```tsx
'use client';
import {
Combobox,
ComboboxContent,
ComboboxEmpty,
ComboboxInput,
ComboboxItem,
ComboboxList,
} from '@/components/ui/combobox';
const frameworks = ['React', 'Vue', 'Angular', 'Svelte', 'Next.js', 'Nuxt'];
export function ComboboxDisabled() {
return (
No frameworks found.
{(item) => (
{item}
)}
);
}
```
### Auto Highlight
```tsx
'use client';
import {
Combobox,
ComboboxContent,
ComboboxEmpty,
ComboboxInput,
ComboboxItem,
ComboboxList,
} from '@/components/ui/combobox';
const frameworks = ['React', 'Vue', 'Angular', 'Svelte', 'Next.js', 'Nuxt'];
export function ComboboxAutoHighlight() {
return (
No frameworks found.
{(item) => (
{item}
)}
);
}
```
# Context Menu
---
title: Context Menu
description: A menu that appears on right-click, providing contextual actions for the target element.
links:
doc: https://base-ui.com/react/components/context-menu
api: https://base-ui.com/react/components/context-menu#api-reference
---
```tsx
'use client';
import {
ContextMenu,
ContextMenuItem,
ContextMenuPopup,
ContextMenuSeparator,
ContextMenuTrigger,
} from '@/components/ui/context-menu';
export function ContextMenuDefault() {
return (
Right click here
BackForwardReloadSave As...Print...
);
}
```
## Installation
```bash
npx shadcn@latest add @fab-ui/context-menu
```
**Install the following dependencies:**
```bash
npm install @base-ui/react
```
**Copy and paste the following code into your project.**
```tsx
'use client';
import { ContextMenu as ContextMenuPrimitive } from '@base-ui/react/context-menu';
import { CheckIcon, ChevronRightIcon, CircleIcon } from 'lucide-react';
import { cn } from '@/lib/utils';
const ContextMenu = ContextMenuPrimitive.Root;
function ContextMenuTrigger({
className,
...props
}: ContextMenuPrimitive.Trigger.Props) {
return (
);
}
function ContextMenuPopup({
align = 'start',
sideOffset = 4,
alignOffset = 0,
className,
...props
}: ContextMenuPrimitive.Popup.Props & {
align?: ContextMenuPrimitive.Positioner.Props['align'];
sideOffset?: ContextMenuPrimitive.Positioner.Props['sideOffset'];
alignOffset?: ContextMenuPrimitive.Positioner.Props['alignOffset'];
}) {
return (
);
}
function ContextMenuGroup({ ...props }: ContextMenuPrimitive.Group.Props) {
return (
);
}
function ContextMenuItem({
className,
...props
}: ContextMenuPrimitive.Item.Props) {
return (
);
}
function ContextMenuCheckboxItem({
className,
children,
checked,
...props
}: ContextMenuPrimitive.CheckboxItem.Props) {
return (
{children}
);
}
function ContextMenuRadioGroup({
...props
}: ContextMenuPrimitive.RadioGroup.Props) {
return (
);
}
function ContextMenuRadioItem({
className,
children,
...props
}: ContextMenuPrimitive.RadioItem.Props) {
return (
{children}
);
}
function ContextMenuGroupLabel({
className,
...props
}: ContextMenuPrimitive.GroupLabel.Props) {
return (
);
}
function ContextMenuSeparator({
className,
...props
}: ContextMenuPrimitive.Separator.Props) {
return (
);
}
function ContextMenuShortcut({
className,
...props
}: React.ComponentProps<'span'>) {
return (
);
}
function ContextMenuSub({ ...props }: ContextMenuPrimitive.SubmenuRoot.Props) {
return (
);
}
function ContextMenuSubTrigger({
className,
children,
...props
}: ContextMenuPrimitive.SubmenuTrigger.Props) {
return (
{children}
);
}
function ContextMenuSubPopup({
align = 'start',
sideOffset = 1,
alignOffset = -4,
className,
...props
}: ContextMenuPrimitive.Popup.Props & {
align?: ContextMenuPrimitive.Positioner.Props['align'];
sideOffset?: ContextMenuPrimitive.Positioner.Props['sideOffset'];
alignOffset?: ContextMenuPrimitive.Positioner.Props['alignOffset'];
}) {
return (
);
}
export {
ContextMenu,
ContextMenuTrigger,
ContextMenuPopup,
ContextMenuGroup,
ContextMenuItem,
ContextMenuCheckboxItem,
ContextMenuRadioGroup,
ContextMenuRadioItem,
ContextMenuGroupLabel,
ContextMenuSeparator,
ContextMenuShortcut,
ContextMenuSub,
ContextMenuSubTrigger,
ContextMenuSubPopup,
};
```
**Update the import paths to match your project setup.**
## Usage
```tsx
import {
ContextMenu,
ContextMenuItem,
ContextMenuPopup,
ContextMenuSeparator,
ContextMenuTrigger,
} from "@/components/ui/context-menu"
```
```tsx
Right click hereBackForwardSave As...
```
## Examples
### With Submenu
```tsx
'use client';
import {
ContextMenu,
ContextMenuItem,
ContextMenuPopup,
ContextMenuSeparator,
ContextMenuSub,
ContextMenuSubPopup,
ContextMenuSubTrigger,
ContextMenuTrigger,
} from '@/components/ui/context-menu';
export function ContextMenuWithSubmenu() {
return (
Right click here
New TabNew WindowMore ToolsSave Page As...Create Shortcut...Developer ToolsSettings
);
}
```
# Dialog
---
title: Dialog
description: A modal window that appears on top of the main content, requiring user interaction.
links:
doc: https://base-ui.com/react/components/dialog
api: https://base-ui.com/react/components/dialog#api-reference
---
```tsx
'use client';
import { Button } from '@/components/ui/button';
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@/components/ui/dialog';
export function DialogDefault() {
return (
);
}
```
## Installation
```bash
npx shadcn@latest add @fab-ui/dialog
```
**Install the following dependencies:**
```bash
npm install @base-ui/react
```
**Copy and paste the following code into your project.**
```tsx
'use client';
import * as React from 'react';
import { Button } from '@/components/ui/button';
import { Dialog as DialogPrimitive } from '@base-ui/react/dialog';
import { XIcon } from 'lucide-react';
import { cn } from '@/lib/utils';
function Dialog({ ...props }: DialogPrimitive.Root.Props) {
return ;
}
function DialogTrigger({ ...props }: DialogPrimitive.Trigger.Props) {
return ;
}
function DialogPortal({ ...props }: DialogPrimitive.Portal.Props) {
return ;
}
function DialogClose({ ...props }: DialogPrimitive.Close.Props) {
return ;
}
function DialogOverlay({
className,
...props
}: DialogPrimitive.Backdrop.Props) {
return (
);
}
function DialogContent({
className,
children,
showCloseButton = true,
...props
}: DialogPrimitive.Popup.Props & {
showCloseButton?: boolean;
}) {
return (
{children}
{showCloseButton && (
Close
}
/>
)}
);
}
function DialogHeader({ className, ...props }: React.ComponentProps<'div'>) {
return (
);
}
function DialogFooter({
className,
showCloseButton = false,
children,
...props
}: React.ComponentProps<'div'> & {
showCloseButton?: boolean;
}) {
return (
{children}
{showCloseButton && (
Close}
/>
)}
);
}
function DialogTitle({ className, ...props }: DialogPrimitive.Title.Props) {
return (
);
}
function DialogDescription({
className,
...props
}: DialogPrimitive.Description.Props) {
return (
);
}
export {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogOverlay,
DialogPortal,
DialogTitle,
DialogTrigger,
};
```
**Update the import paths to match your project setup.**
## Usage
```tsx
import {
Dialog,
DialogDescription,
DialogFooter,
DialogHeader,
DialogPopup,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"
```
```tsx
```
## Examples
### Without Close Button
```tsx
'use client';
import { Button } from '@/components/ui/button';
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@/components/ui/dialog';
export function DialogNoCloseButton() {
return (
);
}
```
### Scrollable Content
```tsx
'use client';
import { Button } from '@/components/ui/button';
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@/components/ui/dialog';
export function DialogScrollableContent() {
return (
);
}
```
# Drawer
---
title: Drawer
description: A panel that slides in from the edge of the screen with swipe-to-dismiss gestures.
links:
doc: https://base-ui.com/react/components/drawer
api: https://base-ui.com/react/components/drawer#api-reference
---
```tsx
'use client';
import { Button } from '@/components/ui/button';
import {
Drawer,
DrawerClose,
DrawerContent,
DrawerDescription,
DrawerFooter,
DrawerHeader,
DrawerPopup,
DrawerTitle,
DrawerTrigger,
} from '@/components/ui/drawer';
export function DrawerDefault() {
return (
}>
Open Drawer
Notifications
You are all caught up. Good job!
}>
Close
);
}
```
## Installation
```bash
npx shadcn@latest add @fab-ui/drawer
```
**Install the following dependencies:**
```bash
npm install @base-ui/react
```
**Copy and paste the following code into your project.**
```tsx
'use client';
import * as React from 'react';
import { Drawer as DrawerPrimitive } from '@base-ui/react/drawer';
import { cn } from '@/lib/utils';
function Drawer({
swipeDirection = 'down',
...props
}: DrawerPrimitive.Root.Props) {
return (
);
}
function DrawerTrigger({ ...props }: DrawerPrimitive.Trigger.Props) {
return ;
}
function DrawerClose({ ...props }: DrawerPrimitive.Close.Props) {
return ;
}
function DrawerProvider({ ...props }: DrawerPrimitive.Provider.Props) {
return ;
}
function DrawerPortal({
className,
...props
}: Omit & {
className?: string;
}) {
return (
);
}
function DrawerOverlay({
className,
...props
}: Omit & {
className?: string;
}) {
return (
);
}
function DrawerViewport({
className,
...props
}: Omit & {
className?: string;
}) {
return (
);
}
function DrawerHandle({ className, ...props }: React.ComponentProps<'div'>) {
return (
);
}
function DrawerContent({
className,
...props
}: Omit & {
className?: string;
}) {
return (
);
}
function DrawerPopup({
className,
children,
container,
showHandle,
unstyled,
overlayClassName,
...props
}: Omit & {
className?: string;
container?: DrawerPrimitive.Portal.Props['container'];
showHandle?: boolean;
unstyled?: boolean;
overlayClassName?: string;
}) {
return (
{!unstyled && showHandle !== false && (
)}
{children}
);
}
function DrawerHeader({ className, ...props }: React.ComponentProps<'div'>) {
return (
);
}
function DrawerFooter({ className, ...props }: React.ComponentProps<'div'>) {
return (
);
}
function DrawerTitle({
className,
...props
}: Omit & {
className?: string;
}) {
return (
);
}
function DrawerDescription({
className,
...props
}: Omit & {
className?: string;
}) {
return (
);
}
function DrawerIndentBackground({
className,
...props
}: DrawerPrimitive.IndentBackground.Props) {
return (
);
}
function DrawerIndent({
className,
...props
}: Omit & {
className?: string;
}) {
return (
);
}
const createDrawerHandle = DrawerPrimitive.createHandle;
export {
Drawer,
DrawerClose,
DrawerContent,
DrawerDescription,
DrawerFooter,
DrawerHandle,
DrawerHeader,
DrawerIndent,
DrawerIndentBackground,
DrawerOverlay,
DrawerPopup,
DrawerPortal,
DrawerProvider,
DrawerTitle,
DrawerTrigger,
DrawerViewport,
createDrawerHandle,
};
```
**Update the import paths to match your project setup.**
## Usage
```tsx
import {
Drawer,
DrawerClose,
DrawerContent,
DrawerDescription,
DrawerFooter,
DrawerHeader,
DrawerTitle,
DrawerTrigger,
} from "@/components/ui/drawer"
```
```tsx
OpenDrawer TitleDrawer description goes here.Close
```
## Examples
### Right Side
Use `swipeDirection="right"` on the `Drawer` component to slide the panel in from the right.
```tsx
'use client';
import { Button } from '@/components/ui/button';
import {
Drawer,
DrawerClose,
DrawerContent,
DrawerDescription,
DrawerFooter,
DrawerHeader,
DrawerPopup,
DrawerTitle,
DrawerTrigger,
} from '@/components/ui/drawer';
export function DrawerRight() {
return (
}>
Open Drawer
Drawer
This is a drawer that slides in from the side. You can swipe to
dismiss it.
}>
Close
);
}
```
### Non-Modal
Set `modal={false}` and `disablePointerDismissal` on the `Drawer` to keep the page interactive while the drawer is open.
```tsx
'use client';
import { Button } from '@/components/ui/button';
import {
Drawer,
DrawerClose,
DrawerContent,
DrawerDescription,
DrawerFooter,
DrawerHeader,
DrawerPopup,
DrawerTitle,
DrawerTrigger,
} from '@/components/ui/drawer';
export function DrawerNonModal() {
return (
}>
Open Non-Modal Drawer
Non-modal Drawer
This drawer does not trap focus and ignores outside clicks. Use
the close button or swipe to dismiss it.
}>
Close
);
}
```
### Nested Drawers
Nest multiple `Drawer` components and manage each with controlled `open` / `onOpenChange` state to create a stacking effect.
```tsx
'use client';
import * as React from 'react';
import { Button } from '@/components/ui/button';
import {
Drawer,
DrawerClose,
DrawerContent,
DrawerDescription,
DrawerPopup,
DrawerTitle,
DrawerTrigger,
} from '@/components/ui/drawer';
export function DrawerNested() {
const [firstOpen, setFirstOpen] = React.useState(false);
const [secondOpen, setSecondOpen] = React.useState(false);
const [thirdOpen, setThirdOpen] = React.useState(false);
return (
{
setFirstOpen(nextOpen);
if (!nextOpen) {
setSecondOpen(false);
setThirdOpen(false);
}
}}
>
}>
Open Drawer Stack
Account
Nested drawers can be styled to stack, while each drawer remains
independently focus managed.
{
setSecondOpen(nextOpen);
if (!nextOpen) {
setThirdOpen(false);
}
}}
>
}
>
Security settings
Security
Review sign-in activity and update your security
preferences.
Passkeys enabled
2FA via authenticator app
3 signed-in devices
}
>
Advanced options
Advanced
This drawer is taller to demonstrate
variable-height stacking.
}
>
Done
}>
Close
}>
Close
);
}
```
### Snap Points
Pass a `snapPoints` array to `Drawer` and use `unstyled` on `DrawerPopup` to apply custom popup styling for snap behavior.
```tsx
'use client';
import * as React from 'react';
import { Button } from '@/components/ui/button';
import {
Drawer,
DrawerClose,
DrawerContent,
DrawerDescription,
DrawerPopup,
DrawerTitle,
DrawerTrigger,
} from '@/components/ui/drawer';
const TOP_MARGIN_REM = 1;
const VISIBLE_SNAP_POINTS_REM = [30];
function toViewportSnapPoint(heightRem: number) {
return `${heightRem + TOP_MARGIN_REM}rem`;
}
const snapPoints = [...VISIBLE_SNAP_POINTS_REM.map(toViewportSnapPoint), 1];
export function DrawerSnapPoints() {
return (
}>
Open Snap Drawer
Snap points
Drag the sheet to snap between a compact peek and a near
full-height view.
{Array.from({ length: 20 }, (_, index) => (
))}
}>
Close
);
}
```
### Action Sheet
Use `DrawerPopup` with `unstyled` and `overlayClassName` to customize the popup and overlay styling for an action sheet layout.
```tsx
'use client';
import * as React from 'react';
import { Button } from '@/components/ui/button';
import {
Drawer,
DrawerClose,
DrawerContent,
DrawerDescription,
DrawerPopup,
DrawerTitle,
DrawerTrigger,
} from '@/components/ui/drawer';
const ACTIONS = [
'Unfollow',
'Mute',
'Add to Favourites',
'Add to Close Friends',
'Restrict',
];
export function DrawerActionSheet() {
const [open, setOpen] = React.useState(false);
return (
}>
Open Action Sheet
Profile actions
Choose an action for this user.
{ACTIONS.map((action, index) => (
{index === 0 && (
Close action sheet
)}
))}
);
}
```
# Dropdown Menu
---
title: Dropdown Menu
description: A menu that appears when clicking a trigger element, providing a list of actions or options.
links:
doc: https://base-ui.com/react/components/menu
api: https://base-ui.com/react/components/menu#api-reference
---
```tsx
'use client';
import { Button } from '@/components/ui/button';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
export function DropdownMenuDefault() {
return (
}>
Open Menu
ProfileSettingsBillingLog out
);
}
```
## Installation
```bash
npx shadcn@latest add @fab-ui/dropdown-menu
```
**Install the following dependencies:**
```bash
npm install @base-ui/react
```
**Copy and paste the following code into your project.**
```tsx
'use client';
import * as React from 'react';
import { Menu as MenuPrimitive } from '@base-ui/react/menu';
import { CheckIcon, ChevronRightIcon } from 'lucide-react';
import { cn } from '@/lib/utils';
function DropdownMenu({ ...props }: MenuPrimitive.Root.Props) {
return ;
}
function DropdownMenuPortal({ ...props }: MenuPrimitive.Portal.Props) {
return ;
}
function DropdownMenuTrigger({ ...props }: MenuPrimitive.Trigger.Props) {
return ;
}
function DropdownMenuContent({
align = 'start',
alignOffset = 0,
side = 'bottom',
sideOffset = 4,
className,
...props
}: MenuPrimitive.Popup.Props &
Pick<
MenuPrimitive.Positioner.Props,
'align' | 'alignOffset' | 'side' | 'sideOffset'
>) {
return (
);
}
function DropdownMenuGroup({ ...props }: MenuPrimitive.Group.Props) {
return ;
}
function DropdownMenuLabel({
className,
inset,
...props
}: MenuPrimitive.GroupLabel.Props & {
inset?: boolean;
}) {
return (
);
}
function DropdownMenuItem({
className,
inset,
variant = 'default',
...props
}: MenuPrimitive.Item.Props & {
inset?: boolean;
variant?: 'default' | 'destructive';
}) {
return (
);
}
function DropdownMenuSub({ ...props }: MenuPrimitive.SubmenuRoot.Props) {
return ;
}
function DropdownMenuSubTrigger({
className,
inset,
children,
...props
}: MenuPrimitive.SubmenuTrigger.Props & {
inset?: boolean;
}) {
return (
{children}
);
}
function DropdownMenuSubContent({
align = 'start',
alignOffset = -3,
side = 'right',
sideOffset = 0,
className,
...props
}: React.ComponentProps) {
return (
);
}
function DropdownMenuCheckboxItem({
className,
children,
checked,
...props
}: MenuPrimitive.CheckboxItem.Props) {
return (
{children}
);
}
function DropdownMenuRadioGroup({ ...props }: MenuPrimitive.RadioGroup.Props) {
return (
);
}
function DropdownMenuRadioItem({
className,
children,
...props
}: MenuPrimitive.RadioItem.Props) {
return (
{children}
);
}
function DropdownMenuSeparator({
className,
...props
}: MenuPrimitive.Separator.Props) {
return (
);
}
function DropdownMenuShortcut({
className,
...props
}: React.ComponentProps<'span'>) {
return (
);
}
export {
DropdownMenu,
DropdownMenuPortal,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuLabel,
DropdownMenuItem,
DropdownMenuCheckboxItem,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuSub,
DropdownMenuSubTrigger,
DropdownMenuSubContent,
};
```
**Update the import paths to match your project setup.**
## Usage
```tsx
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
```
```tsx
OpenProfileSettingsLog out
```
## Examples
### With Submenu
```tsx
'use client';
import { Button } from '@/components/ui/button';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
export function DropdownMenuWithSubmenu() {
return (
}>
Open Menu
New File
⌘N
Open File
⌘OShareEmailMessagesMore optionsCopy LinkQR CodeAirDrop
Print
⌘P
);
}
```
### Checkboxes
```tsx
'use client';
import * as React from 'react';
import { Button } from '@/components/ui/button';
import {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuLabel,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
export function DropdownMenuCheckboxes() {
const [emailNotifs, setEmailNotifs] = React.useState(true);
const [pushNotifs, setPushNotifs] = React.useState(false);
const [smsNotifs, setSmsNotifs] = React.useState(false);
return (
}>
Notifications
Notify me via
Email
Push notifications
SMS
);
}
```
### Radio Group
```tsx
'use client';
import * as React from 'react';
import { Button } from '@/components/ui/button';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuLabel,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
export function DropdownMenuRadioGroupDemo() {
const [sortBy, setSortBy] = React.useState('date');
return (
}>
Sort By
Sort orderNameDateSize
);
}
```
# Field
---
title: Field
description: A form field wrapper that associates a label, description, and error message with an input.
links:
doc: https://base-ui.com/react/components/field
api: https://base-ui.com/react/components/field#api-reference
---
```tsx
'use client';
import { Field, FieldLabel } from '@/components/ui/field';
import { Input } from '@/components/ui/input';
export function FieldDefault() {
return (
Email
);
}
```
## Installation
```bash
npx shadcn@latest add @fab-ui/field
```
**Install the following dependencies:**
```bash
npm install @base-ui/react
```
**Copy and paste the following code into your project.**
```tsx
'use client';
import { Field as FieldPrimitive } from '@base-ui/react/field';
import { cn } from '@/lib/utils';
function Field({ className, ...props }: FieldPrimitive.Root.Props) {
return (
);
}
function FieldLabel({ className, ...props }: FieldPrimitive.Label.Props) {
return (
);
}
function FieldDescription({
className,
...props
}: FieldPrimitive.Description.Props) {
return (
);
}
function FieldError({ className, ...props }: FieldPrimitive.Error.Props) {
return (
);
}
const FieldControl = FieldPrimitive.Control;
const FieldValidity = FieldPrimitive.Validity;
export {
Field,
FieldLabel,
FieldDescription,
FieldError,
FieldControl,
FieldValidity,
};
```
**Update the import paths to match your project setup.**
## Usage
```tsx
import {
Field,
FieldDescription,
FieldError,
FieldLabel,
} from "@/components/ui/field"
```
```tsx
EmailWe'll never share your email.Please enter a valid email address.
```
## Examples
### With Description
```tsx
'use client';
import {
Field,
FieldDescription,
FieldLabel,
} from '@/components/ui/field';
import { Input } from '@/components/ui/input';
export function FieldWithDescription() {
return (
Username
This will be your public display name.
);
}
```
# Fieldset
---
title: Fieldset
description: A container for grouping related form fields with a legend.
links:
doc: https://base-ui.com/react/components/fieldset
api: https://base-ui.com/react/components/fieldset#api-reference
---
```tsx
'use client';
import { Field, FieldLabel } from '@/components/ui/field';
import { Fieldset, FieldsetLegend } from '@/components/ui/fieldset';
import { Input } from '@/components/ui/input';
export function FieldsetDefault() {
return (
);
}
```
## Installation
```bash
npx shadcn@latest add @fab-ui/fieldset
```
**Install the following dependencies:**
```bash
npm install @base-ui/react
```
**Copy and paste the following code into your project.**
```tsx
'use client';
import { Fieldset as FieldsetPrimitive } from '@base-ui/react/fieldset';
import { cn } from '@/lib/utils';
function Fieldset({ className, ...props }: FieldsetPrimitive.Root.Props) {
return (
);
}
function FieldsetLegend({
className,
...props
}: FieldsetPrimitive.Legend.Props) {
return (
);
}
export { Fieldset, FieldsetLegend };
```
**Update the import paths to match your project setup.**
## Usage
```tsx
import { Fieldset, FieldsetLegend } from "@/components/ui/fieldset"
```
```tsx
```
# Form
---
title: Form
description: A form container that handles validation and submission.
links:
doc: https://base-ui.com/react/components/form
api: https://base-ui.com/react/components/form#api-reference
---
```tsx
'use client';
import { Button } from '@/components/ui/button';
import {
Field,
FieldDescription,
FieldLabel,
} from '@/components/ui/field';
import { Form } from '@/components/ui/form';
import { Input } from '@/components/ui/input';
export function FormDefault() {
return (
);
}
```
## Installation
```bash
npx shadcn@latest add @fab-ui/form
```
**Install the following dependencies:**
```bash
npm install @base-ui/react
```
**Copy and paste the following code into your project.**
```tsx
'use client';
import { Form as FormPrimitive } from '@base-ui/react/form';
import { cn } from '@/lib/utils';
function Form({ className, ...props }: FormPrimitive.Props) {
return (
);
}
export { Form };
```
**Update the import paths to match your project setup.**
## Usage
```tsx
import { Form } from "@/components/ui/form"
```
```tsx
```
# Hover Card
---
title: Hover Card
description: A card that appears on hover to preview content.
links:
doc: https://base-ui.com/react/components/preview-card
api: https://base-ui.com/react/components/preview-card#api-reference
---
```tsx
'use client';
import {
HoverCard,
HoverCardContent,
HoverCardTrigger,
} from '@/components/ui/hover-card';
export function HoverCardDefault() {
return (
Hover over me
Hover Card
This is a hover card that appears on hover. It can contain any
content you want to display.
);
}
```
## Installation
```bash
npx shadcn@latest add @fab-ui/hover-card
```
**Install the following dependencies:**
```bash
npm install @base-ui/react
```
**Copy and paste the following code into your project.**
```tsx
'use client';
import { PreviewCard as PreviewCardPrimitive } from '@base-ui/react/preview-card';
import { cn } from '@/lib/utils';
function HoverCard({ ...props }: PreviewCardPrimitive.Root.Props) {
return ;
}
function HoverCardTrigger({ ...props }: PreviewCardPrimitive.Trigger.Props) {
return (
);
}
function HoverCardContent({
className,
side = 'bottom',
sideOffset = 4,
align = 'center',
alignOffset = 4,
...props
}: PreviewCardPrimitive.Popup.Props &
Pick<
PreviewCardPrimitive.Positioner.Props,
'align' | 'alignOffset' | 'side' | 'sideOffset'
>) {
return (
);
}
export { HoverCard, HoverCardTrigger, HoverCardContent };
```
**Update the import paths to match your project setup.**
## Usage
```tsx
import {
HoverCard,
HoverCardContent,
HoverCardTrigger,
} from "@/components/ui/hover-card"
```
```tsx
Hover over me
Preview content goes here.
```
## Examples
### Sides
```tsx
'use client';
import { Button } from '@/components/ui/button';
import {
HoverCard,
HoverCardContent,
HoverCardTrigger,
} from '@/components/ui/hover-card';
const HOVER_CARD_SIDES = ['left', 'top', 'bottom', 'right'] as const;
export function HoverCardSides() {
return (
{HOVER_CARD_SIDES.map((side) => (
{side}
}
/>
Hover Card
This hover card appears on the {side} side of the trigger.
))}
);
}
```
# Input Group
---
title: Input Group
description: An input with addons like icons, buttons, or text on either side.
---
```tsx
'use client';
import {
InputGroup,
InputGroupAddon,
InputGroupInput,
InputGroupText,
InputGroupTextarea,
} from '@/components/ui/input-group';
export function InputGroupDefault() {
return (
https://@years
Max 200 characters
);
}
```
## Installation
```bash
npx shadcn@latest add @fab-ui/input-group
```
**Install the following dependencies:**
```bash
npm install @base-ui/react
```
**Copy and paste the following code into your project.**
```tsx
'use client';
import * as React from 'react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Textarea } from '@/components/ui/textarea';
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@/lib/utils';
function InputGroup({ className, ...props }: React.ComponentProps<'div'>) {
return (
);
}
```
# Select
---
title: Select
description: A dropdown menu for selecting a value from a list of options.
links:
doc: https://base-ui.com/react/components/select
api: https://base-ui.com/react/components/select#api-reference
---
```tsx
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
const fonts = [
{ label: 'Select font', value: null },
{ label: 'Sans-serif', value: 'sans' },
{ label: 'Serif', value: 'serif' },
{ label: 'Monospace', value: 'mono' },
{ label: 'Cursive', value: 'cursive' },
];
export function SelectDemo() {
return (
);
}
```
## Installation
```bash
npx shadcn@latest add @fab-ui/select
```
**Install the following dependencies:**
```bash
npm install @base-ui/react
```
**Copy and paste the following code into your project.**
```tsx
'use client';
import * as React from 'react';
import { Select as SelectPrimitive } from '@base-ui/react/select';
import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from 'lucide-react';
import { cn } from '@/lib/utils';
const Select = SelectPrimitive.Root;
function SelectGroup({ className, ...props }: SelectPrimitive.Group.Props) {
return (
);
}
function SelectValue({ className, ...props }: SelectPrimitive.Value.Props) {
return (
);
}
function SelectTrigger({
className,
size = 'default',
children,
...props
}: SelectPrimitive.Trigger.Props & {
size?: 'sm' | 'default';
}) {
return (
{children}
}
/>
);
}
function SelectContent({
className,
children,
side = 'bottom',
sideOffset = 4,
align = 'center',
alignOffset = 0,
alignItemWithTrigger = true,
...props
}: SelectPrimitive.Popup.Props &
Pick<
SelectPrimitive.Positioner.Props,
'align' | 'alignOffset' | 'side' | 'sideOffset' | 'alignItemWithTrigger'
>) {
return (
{children}
);
}
function SelectLabel({
className,
...props
}: SelectPrimitive.GroupLabel.Props) {
return (
);
}
function SelectItem({
className,
children,
...props
}: SelectPrimitive.Item.Props) {
return (
{children}
}
/>
);
}
function SelectSeparator({
className,
...props
}: SelectPrimitive.Separator.Props) {
return (
);
}
function SelectScrollUpButton({
className,
...props
}: React.ComponentProps) {
return (
);
}
function SelectScrollDownButton({
className,
...props
}: React.ComponentProps) {
return (
);
}
export {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectScrollDownButton,
SelectScrollUpButton,
SelectSeparator,
SelectTrigger,
SelectValue,
};
```
**Update the import paths to match your project setup.**
## Usage
```tsx
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
```
```tsx
```
## Examples
### Grouped
```tsx
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectSeparator,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
export function SelectGroups() {
const fruits = [
{ label: 'Apple', value: 'apple' },
{ label: 'Banana', value: 'banana' },
{ label: 'Blueberry', value: 'blueberry' },
];
const vegetables = [
{ label: 'Carrot', value: 'carrot' },
{ label: 'Broccoli', value: 'broccoli' },
{ label: 'Spinach', value: 'spinach' },
];
const allItems = [
{ label: 'Select a fruit', value: null },
...fruits,
...vegetables,
];
return (
);
}
```
# Separator
---
title: Separator
description: A visual divider between content sections.
links:
doc: https://base-ui.com/react/components/separator
api: https://base-ui.com/react/components/separator#api-reference
---
```tsx
'use client';
import { Separator } from '@/components/ui/separator';
export function SeparatorDefault() {
return (
Fab UI
An open-source UI component library.
Blog
Docs
Source
);
}
```
## Installation
```bash
npx shadcn@latest add @fab-ui/separator
```
**Install the following dependencies:**
```bash
npm install @base-ui/react
```
**Copy and paste the following code into your project.**
```tsx
'use client';
import { Separator as SeparatorPrimitive } from '@base-ui/react/separator';
import { cn } from '@/lib/utils';
function Separator({
className,
orientation = 'horizontal',
...props
}: SeparatorPrimitive.Props) {
return (
);
}
export { Separator };
```
**Update the import paths to match your project setup.**
## Usage
```tsx
import { Separator } from "@/components/ui/separator"
```
```tsx
```
# Slider
---
title: Slider
description: A control for selecting a value from a range by dragging a thumb.
links:
doc: https://base-ui.com/react/components/slider
api: https://base-ui.com/react/components/slider#api-reference
---
```tsx
'use client';
import { Slider } from '@/components/ui/slider';
export function SliderDefault() {
return ;
}
```
## Installation
```bash
npx shadcn@latest add @fab-ui/slider
```
**Install the following dependencies:**
```bash
npm install @base-ui/react
```
**Copy and paste the following code into your project.**
```tsx
'use client';
import * as React from 'react';
import { Slider as SliderPrimitive } from '@base-ui/react/slider';
import { cn } from '@/lib/utils';
function Slider({
className,
defaultValue,
value,
min = 0,
max = 100,
...props
}: SliderPrimitive.Root.Props) {
const _values = React.useMemo(
() =>
Array.isArray(value)
? value
: Array.isArray(defaultValue)
? defaultValue
: [min, max],
[value, defaultValue, min, max]
);
return (
{Array.from({ length: _values.length }, (_, index) => (
))}
);
}
export { Slider };
```
**Update the import paths to match your project setup.**
## Usage
```tsx
import { Slider, SliderValue } from "@/components/ui/slider"
```
```tsx
```
## Examples
### With Value
```tsx
'use client';
import { Slider } from '@/components/ui/slider';
export function SliderWithValue() {
return (
);
}
```
# Switch
---
title: Switch
description: A toggle control for switching between two states.
links:
doc: https://base-ui.com/react/components/switch
api: https://base-ui.com/react/components/switch#api-reference
---
```tsx
'use client';
import { Switch } from '@/components/ui/switch';
export function SwitchDefault() {
return ;
}
```
## Installation
```bash
npx shadcn@latest add @fab-ui/switch
```
**Install the following dependencies:**
```bash
npm install @base-ui/react
```
**Copy and paste the following code into your project.**
```tsx
'use client';
import { Switch as SwitchPrimitive } from '@base-ui/react/switch';
import { cn } from '@/lib/utils';
function Switch({
className,
size = 'default',
...props
}: SwitchPrimitive.Root.Props & {
size?: 'sm' | 'default';
}) {
return (
);
}
export { Switch };
```
**Update the import paths to match your project setup.**
## Usage
```tsx
import { Switch } from "@/components/ui/switch"
```
```tsx
```
## Examples
### With Label
```tsx
'use client';
import { Label } from '@/components/ui/label';
import { Switch } from '@/components/ui/switch';
export function SwitchWithLabel() {
return (
);
}
```
# Tabs
---
title: Tabs
description: A set of layered sections of content displayed one at a time.
links:
doc: https://base-ui.com/react/components/tabs
api: https://base-ui.com/react/components/tabs#api-reference
---
```tsx
'use client';
import {
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from '@/components/ui/tabs';
export function TabsDefault() {
return (
Make changes to your account.
Change your password here.
Adjust your settings here.
);
}
```
## Installation
```bash
npx shadcn@latest add @fab-ui/tabs
```
**Install the following dependencies:**
```bash
npm install @base-ui/react
```
**Copy and paste the following code into your project.**
```tsx
'use client';
import { Tabs as TabsPrimitive } from '@base-ui/react/tabs';
import { cn } from '@/lib/utils';
type TabsVariant = 'default' | 'underline';
function Tabs({ className, ...props }: TabsPrimitive.Root.Props) {
return (
);
}
function TabsList({
variant = 'default',
className,
children,
...props
}: TabsPrimitive.List.Props & {
variant?: TabsVariant;
}) {
return (
{children}
);
}
function TabsTrigger({ className, ...props }: TabsPrimitive.Tab.Props) {
return (
);
}
function TabsContent({ className, ...props }: TabsPrimitive.Panel.Props) {
return (
);
}
export { Tabs, TabsList, TabsTrigger, TabsContent };
```
**Update the import paths to match your project setup.**
## Usage
```tsx
import { Tabs, TabsList, TabsTrigger, TabsPanel } from "@/components/ui/tabs"
```
```tsx
Account content
Password content
```
## Examples
### Underline
Use `variant="underline"` on the `TabsList` for an underline style indicator.
```tsx
'use client';
import {
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from '@/components/ui/tabs';
export function TabsUnderline() {
return (
View your dashboard overview.
Analyze your data and metrics.
Generate and view reports.
);
}
```
# Textarea
---
title: Textarea
description: A multi-line text input field.
---
```tsx
'use client';
import { Textarea } from '@/components/ui/textarea';
export function TextareaDefault() {
return ;
}
```
## Installation
```bash
npx shadcn@latest add @fab-ui/textarea
```
**Copy and paste the following code into your project.**
```tsx
import * as React from 'react';
import { cn } from '@/lib/utils';
function Textarea({ className, ...props }: React.ComponentProps<'textarea'>) {
return (
);
}
export { Textarea };
```
**Update the import paths to match your project setup.**
## Usage
```tsx
import { Textarea } from "@/components/ui/textarea"
```
```tsx
```
## Examples
### With Label
```tsx
'use client';
import {
Field,
FieldDescription,
FieldLabel,
} from '@/components/ui/field';
import { Textarea } from '@/components/ui/textarea';
export function TextareaWithLabel() {
return (
Bio
You can @mention other users and organizations.
);
}
```
# Toggle Group
---
title: Toggle Group
description: A set of toggle buttons that allows single or multiple selections.
links:
doc: https://base-ui.com/react/components/toggle-group
api: https://base-ui.com/react/components/toggle-group#api-reference
---
```tsx
'use client';
import {
ToggleGroup,
ToggleGroupItem,
} from '@/components/ui/toggle-group';
import { AlignCenter, AlignLeft, AlignRight } from 'lucide-react';
export function ToggleGroupDefault() {
return (
);
}
```
## Installation
```bash
npx shadcn@latest add @fab-ui/toggle-group
```
**Install the following dependencies:**
```bash
npm install @base-ui/react
```
**Copy and paste the following code into your project.**
```tsx
'use client';
import * as React from 'react';
import { Toggle, toggleVariants } from '@/components/ui/toggle';
import { ToggleGroup as ToggleGroupPrimitive } from '@base-ui/react/toggle-group';
import { type VariantProps } from 'class-variance-authority';
import { cn } from '@/lib/utils';
const ToggleGroupContext = React.createContext<
VariantProps
>({
size: 'default',
variant: 'default',
});
function ToggleGroup({
className,
variant,
size,
children,
...props
}: ToggleGroupPrimitive.Props & VariantProps) {
return (
{children}
);
}
function ToggleGroupItem({
className,
children,
variant,
size,
...props
}: React.ComponentProps & VariantProps) {
const context = React.useContext(ToggleGroupContext);
return (
{children}
);
}
export { ToggleGroup, ToggleGroupItem };
```
**Update the import paths to match your project setup.**
## Usage
```tsx
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"
```
```tsx
LeftCenterRight
```
## Examples
### Multiple
Use `multiple` to allow multiple items to be selected.
```tsx
'use client';
import {
ToggleGroup,
ToggleGroupItem,
} from '@/components/ui/toggle-group';
import { Bold, Italic, Underline } from 'lucide-react';
export function ToggleGroupMultiple() {
return (
);
}
```
# Toggle
---
title: Toggle
description: A two-state button that can be either on or off.
links:
doc: https://base-ui.com/react/components/toggle
api: https://base-ui.com/react/components/toggle#api-reference
---
```tsx
'use client';
import { Toggle } from '@/components/ui/toggle';
import { Bold } from 'lucide-react';
export function ToggleDefault() {
return (
);
}
```
## Installation
```bash
npx shadcn@latest add @fab-ui/toggle
```
**Install the following dependencies:**
```bash
npm install @base-ui/react
```
**Copy and paste the following code into your project.**
```tsx
'use client';
import { Toggle as TogglePrimitive } from '@base-ui/react/toggle';
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@/lib/utils';
const toggleVariants = cva(
"hover:text-foreground aria-pressed:bg-muted focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive data-[state=on]:bg-muted gap-1 rounded-lg text-sm font-medium transition-all [&_svg:not([class*='size-'])]:size-4 group/toggle hover:bg-muted inline-flex items-center justify-center whitespace-nowrap outline-none focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
{
variants: {
variant: {
default: 'bg-transparent',
outline: 'border-input hover:bg-muted border bg-transparent',
},
size: {
default: 'h-8 min-w-8 px-2',
sm: 'h-7 min-w-7 rounded-[min(var(--radius-md),12px)] px-1.5 text-[0.8rem]',
lg: 'h-9 min-w-9 px-2.5',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
}
);
function Toggle({
className,
variant = 'default',
size = 'default',
...props
}: TogglePrimitive.Props & VariantProps) {
return (
);
}
export { Toggle, toggleVariants };
```
**Update the import paths to match your project setup.**
## Usage
```tsx
import { Toggle } from "@/components/ui/toggle"
```
```tsx
```
## Examples
### Outline
```tsx
'use client';
import { Toggle } from '@/components/ui/toggle';
import { Italic } from 'lucide-react';
export function ToggleOutline() {
return (
);
}
```
### With Text
```tsx
'use client';
import { Toggle } from '@/components/ui/toggle';
import { Italic } from 'lucide-react';
export function ToggleWithText() {
return (
Italic
);
}
```
# Toolbar
---
title: Toolbar
description: A container for grouping a set of buttons and controls.
links:
doc: https://base-ui.com/react/components/toolbar#api-reference
---
```tsx
'use client';
import { Button } from '@/components/ui/button';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import {
ToggleGroup,
ToggleGroupItem,
} from '@/components/ui/toggle-group';
import {
Toolbar,
ToolbarButton,
ToolbarGroup,
ToolbarSeparator,
} from '@/components/ui/toolbar';
import {
Tooltip,
TooltipPopup,
TooltipProvider,
TooltipTrigger,
} from '@/components/ui/tooltip';
import {
AlignCenter,
AlignLeft,
AlignRight,
DollarSign,
Percent,
} from 'lucide-react';
const fonts = [
{ label: 'Helvetica', value: 'helvetica' },
{ label: 'Arial', value: 'arial' },
];
export function ToolbarDefault() {
return (
}
>
}
/>
Align left}
>
}
/>
Align center}
>
}
/>
Align right}
>
}
/>
Format as currency}
>
}
/>
Format as percent
);
}
```
## Installation
```bash
npx shadcn@latest add @fab-ui/toolbar
```
**Install the following dependencies:**
```bash
npm install @base-ui/react
```
**Copy and paste the following code into your project.**
```tsx
'use client';
import { Toolbar as ToolbarPrimitive } from '@base-ui/react/toolbar';
import { cn } from '@/lib/utils';
function Toolbar({ className, ...props }: ToolbarPrimitive.Root.Props) {
return (
);
}
function ToolbarButton({ className, ...props }: ToolbarPrimitive.Button.Props) {
return (
);
}
function ToolbarLink({ className, ...props }: ToolbarPrimitive.Link.Props) {
return (
);
}
function ToolbarInput({ className, ...props }: ToolbarPrimitive.Input.Props) {
return (
);
}
function ToolbarGroup({ className, ...props }: ToolbarPrimitive.Group.Props) {
return (
);
}
function ToolbarSeparator({
className,
...props
}: ToolbarPrimitive.Separator.Props) {
return (
);
}
export {
Toolbar,
ToolbarGroup,
ToolbarSeparator,
ToolbarButton,
ToolbarLink,
ToolbarInput,
};
```
**Update the import paths to match your project setup.**
## Usage
```tsx
import { Button } from "@/components/ui/button"
import { Toggle } from "@/components/ui/toggle"
import {
Toolbar,
ToolbarButton,
ToolbarGroup,
ToolbarSeparator,
} from "@/components/ui/toolbar"
```
```tsx
}>Bold
}>Underline
}>Save
```
# Tooltip
---
title: Tooltip
description: A popup that displays information related to an element when the element receives keyboard focus or the mouse hovers over it.
links:
doc: https://base-ui.com/react/components/tooltip
api: https://base-ui.com/react/components/tooltip#api-reference
---
```tsx
'use client';
import { Button } from '@/components/ui/button';
import {
Tooltip,
TooltipPopup,
TooltipProvider,
TooltipTrigger,
} from '@/components/ui/tooltip';
export function TooltipDefault() {
return (
Hover me} />
Add to library
);
}
```
## Installation
```bash
npx shadcn@latest add @fab-ui/tooltip
```
**Install the following dependencies:**
```bash
npm install @base-ui/react
```
**Copy and paste the following code into your project.**
```tsx
import { Tooltip as TooltipPrimitive } from '@base-ui/react/tooltip';
import { cn } from '@/lib/utils';
const TooltipProvider = TooltipPrimitive.Provider;
const Tooltip = TooltipPrimitive.Root;
function TooltipTrigger(props: TooltipPrimitive.Trigger.Props) {
return ;
}
function ArrowSvg(props: React.ComponentProps<'svg'>) {
return (
);
}
function TooltipArrow({ className, ...props }: TooltipPrimitive.Arrow.Props) {
return (
);
}
function TooltipPopup({
className,
align = 'center',
sideOffset = 9,
side = 'top',
children,
...props
}: TooltipPrimitive.Popup.Props & {
align?: TooltipPrimitive.Positioner.Props['align'];
side?: TooltipPrimitive.Positioner.Props['side'];
sideOffset?: TooltipPrimitive.Positioner.Props['sideOffset'];
}) {
return (
{children}
);
}
export { TooltipProvider, Tooltip, TooltipTrigger, TooltipPopup };
```
**Update the import paths to match your project setup.**
## Usage
```tsx
import {
Tooltip,
TooltipPopup,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip"
```
```tsx
HoverTooltip content
```
## Examples
### Positions
Use the `side` prop on `TooltipPopup` to control the position.
```tsx
'use client';
import { Button } from '@/components/ui/button';
import {
Tooltip,
TooltipPopup,
TooltipProvider,
TooltipTrigger,
} from '@/components/ui/tooltip';
export function TooltipPositions() {
return (
Top} />
Tooltip on topRight} />
Tooltip on rightBottom} />
Tooltip on bottomLeft} />
Tooltip on left
);
}
```
# Liquid Metal Avatar
---
title: Liquid Metal Avatar
description: An avatar with an animated liquid metal shader effect.
---
```tsx
import {
AvatarFallback,
AvatarImage,
LiquidMetalAvatar,
} from '@/components/ui/liquid-metal-avatar';
export function LiquidMetalAvatarDefault() {
return (
JD
);
}
```
## Installation
```bash
npx shadcn@latest add @fab-ui/liquid-metal-avatar
```
**Install the following dependencies:**
```bash
npm install @paper-design/shaders-react
```
**Copy and paste the following code into your project.**
```tsx
'use client';
import {
Avatar,
AvatarFallback,
AvatarImage,
} from '@/components/ui/avatar';
import { LiquidMetal, LiquidMetalProps } from '@paper-design/shaders-react';
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@/lib/utils';
const avatarVariants = cva(
'relative flex items-center justify-center rounded-full overflow-visible bg-transparent',
{
variants: {
size: {
sm: 'size-8',
md: 'size-12',
lg: 'size-16',
},
},
defaultVariants: { size: 'md' },
}
);
const innerVariants = cva(
'absolute rounded-full bg-primary-foreground inset-shadow-lg',
{
variants: {
size: {
sm: 'inset-[1px]',
md: 'inset-[2px]',
lg: 'inset-[3px]',
},
},
defaultVariants: { size: 'md' },
}
);
type ShaderProps = Omit;
type LiquidMetalAvatarProps = React.ComponentProps<'div'> &
Partial &
VariantProps;
function LiquidMetalAvatar({
size,
className,
children,
speed = 0.6,
repetition = 4,
softness = 0.5,
shiftRed = 0.3,
shiftBlue = 0.3,
distortion = 0,
contour = 0,
angle = 45,
scale = 8,
offsetX = 0.1,
offsetY = -0.1,
...props
}: LiquidMetalAvatarProps) {
return (
{children}
);
}
export { LiquidMetalAvatar, AvatarImage, AvatarFallback };
```
**Update the import paths to match your project setup.**
## Usage
```tsx
import {
LiquidMetalAvatar,
AvatarImage,
AvatarFallback,
} from "@/components/ui/liquid-metal-avatar"
```
```tsx
JD
```
## Examples
### Sizes
```tsx
import {
AvatarFallback,
AvatarImage,
LiquidMetalAvatar,
} from '@/components/ui/liquid-metal-avatar';
export function LiquidMetalAvatarSizes() {
return (
SMMDLG
);
}
```
### Custom Effect
```tsx
import {
AvatarFallback,
AvatarImage,
LiquidMetalAvatar,
} from '@/components/ui/liquid-metal-avatar';
export function LiquidMetalAvatarCustom() {
return (
AB
);
}
```
### Fallback Only
```tsx
import {
AvatarFallback,
LiquidMetalAvatar,
} from '@/components/ui/liquid-metal-avatar';
export function LiquidMetalAvatarFallback() {
return (
JD
);
}
```
## API Reference
### Props
| Prop | Type | Default | Description |
| ------------ | ---------------------- | ------- | ----------------------------------------- |
| `children` | `ReactNode` | - | Avatar content (`AvatarImage`, `AvatarFallback`). |
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | The size of the avatar. |
| `speed` | `number` | `0.4` | Animation speed of the shader effect. |
| `repetition` | `number` | `6` | Number of pattern repetitions. |
| `softness` | `number` | `0.5` | Softness of the metallic highlights. |
| `shiftRed` | `number` | `0.3` | Red color shift amount. |
| `shiftBlue` | `number` | `0.3` | Blue color shift amount. |
| `distortion` | `number` | `0` | Distortion amount applied to the pattern. |
| `contour` | `number` | `0` | Contour intensity. |
| `angle` | `number` | `45` | Angle of the shader pattern in degrees. |
| `scale` | `number` | `4` | Scale of the shader pattern. |
| `offsetX` | `number` | `0.1` | Horizontal offset of the pattern. |
| `offsetY` | `number` | `-0.1` | Vertical offset of the pattern. |
# Liquid Metal Button
---
title: Liquid Metal Button
description: A button with an animated liquid metal shader effect.
---
This button uses `bg-primary-foreground` so the liquid metal effect blends
naturally with its surroundings—dark on dark in dark mode, light on light in
light mode.
```tsx
import { LiquidMetalButton } from '@/components/ui/liquid-metal-button';
export function LiquidMetalButtonDefault() {
return Click me;
}
```
## Installation
```bash
npx shadcn@latest add @fab-ui/liquid-metal-button
```
**Install the following dependencies:**
```bash
npm install @paper-design/shaders-react
```
**Copy and paste the following code into your project.**
```tsx
'use client';
import { Button } from '@/components/ui/button';
import { LiquidMetal, LiquidMetalProps } from '@paper-design/shaders-react';
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@/lib/utils';
const buttonVariants = cva(
'relative flex items-center justify-center bg-transparent border-none cursor-pointer outline-none rounded-full overflow-hidden font-medium whitespace-nowrap text-primary',
{
variants: {
size: {
sm: 'min-w-28 h-9 px-4 text-xs',
md: 'min-w-35.5 h-11.5 px-5 text-sm',
lg: 'min-w-44 h-14 px-6 text-base',
},
},
defaultVariants: { size: 'md' },
}
);
type ShaderProps = Omit;
type LiquidMetalButtonProps = Omit<
React.ComponentProps,
'variant' | 'size'
> &
Partial &
VariantProps;
export function LiquidMetalButton({
children,
size,
className,
ref,
speed = 0.6,
repetition = 4,
softness = 0.5,
shiftRed = 0.3,
shiftBlue = 0.3,
distortion = 0,
contour = 0,
angle = 45,
scale = 8,
offsetX = 0.1,
offsetY = -0.1,
...props
}: LiquidMetalButtonProps) {
return (
);
}
```
**Update the import paths to match your project setup.**
## Usage
```tsx
import { LiquidMetalButton } from "@/components/ui/liquid-metal-button"
```
```tsx
Click me
```
## Examples
### Sizes
```tsx
import { LiquidMetalButton } from '@/components/ui/liquid-metal-button';
export function LiquidMetalButtonSizes() {
return (
SmallMediumLarge
);
}
```
### Custom Effect
```tsx
import { LiquidMetalButton } from '@/components/ui/liquid-metal-button';
export function LiquidMetalButtonCustom() {
return (
Custom Effect
);
}
```
## API Reference
### Props
| Prop | Type | Default | Description |
| ------------ | ---------------------- | ------- | ----------------------------------------- |
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | The size of the button. |
| `speed` | `number` | `0.6` | Animation speed of the shader effect. |
| `repetition` | `number` | `4` | Number of pattern repetitions. |
| `softness` | `number` | `0.5` | Softness of the metallic highlights. |
| `shiftRed` | `number` | `0.3` | Red color shift amount. |
| `shiftBlue` | `number` | `0.3` | Blue color shift amount. |
| `distortion` | `number` | `0` | Distortion amount applied to the pattern. |
| `contour` | `number` | `0` | Contour intensity. |
| `angle` | `number` | `45` | Angle of the shader pattern in degrees. |
| `scale` | `number` | `8` | Scale of the shader pattern. |
| `offsetX` | `number` | `0.1` | Horizontal offset of the pattern. |
| `offsetY` | `number` | `-0.1` | Vertical offset of the pattern. |
# Liquid Metal Card
---
title: Liquid Metal Card
description: A card with an animated liquid metal shader effect.
---
This card uses `bg-card` so the liquid metal effect blends
naturally with its surroundings—dark on dark in dark mode, light on light in
light mode.
```tsx
import { Button } from '@/components/ui/button';
import {
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
LiquidMetalCard,
} from '@/components/ui/liquid-metal-card';
export function LiquidMetalCardDefault() {
return (
Create projectDeploy your new project in one-click.
Your new project will be created with the default settings. You can
customize it later in the project settings.
);
}
```
## Installation
```bash
npx shadcn@latest add @fab-ui/liquid-metal-card
```
**Install the following dependencies:**
```bash
npm install @paper-design/shaders-react
```
**Copy and paste the following code into your project.**
```tsx
'use client';
import {
Card,
CardAction,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from '@/components/ui/card';
import { LiquidMetal, LiquidMetalProps } from '@paper-design/shaders-react';
import { cn } from '@/lib/utils';
type ShaderProps = Omit;
type LiquidMetalCardProps = React.ComponentProps &
Partial;
function LiquidMetalCard({
className,
children,
speed = 0.6,
repetition = 4,
softness = 0.5,
shiftRed = 0.3,
shiftBlue = 0.3,
distortion = 0,
contour = 0,
angle = 45,
scale = 8,
offsetX = 0.1,
offsetY = -0.1,
...props
}: LiquidMetalCardProps) {
return (
{children}
);
}
export {
LiquidMetalCard,
CardHeader,
CardFooter,
CardTitle,
CardAction,
CardDescription,
CardContent,
};
```
**Update the import paths to match your project setup.**
## Usage
```tsx
import {
LiquidMetalCard,
CardHeader,
CardTitle,
CardDescription,
CardContent,
CardFooter,
} from "@/components/ui/liquid-metal-card"
```
```tsx
Card TitleCard Description
Card Content
Card Footer
```
## Examples
### Custom Effect
```tsx
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import {
CardAction,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
LiquidMetalCard,
} from '@/components/ui/liquid-metal-card';
export function LiquidMetalCardCustom() {
return (
NotificationsYou have 3 unread messages.New
Customize the liquid metal effect with props like speed, repetition,
softness, and color shifts.
);
}
```
### Player
```tsx
import { Button } from '@/components/ui/button';
import {
CardContent,
CardDescription,
CardHeader,
CardTitle,
LiquidMetalCard,
} from '@/components/ui/liquid-metal-card';
import { Play, SkipBack, SkipForward } from 'lucide-react';
export function LiquidMetalCardPlayer() {
return (
The SummoningSleep Token · Take Me Back to Eden
2:15
6:46
);
}
```
### Login
```tsx
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import {
CardContent,
CardDescription,
CardHeader,
CardTitle,
LiquidMetalCard,
} from '@/components/ui/liquid-metal-card';
import { User } from 'lucide-react';
import { Icons } from '@/components/icons';
export function LiquidMetalCardLogin() {
return (
Welcome backSign in to your account
or continue with
);
}
```
## API Reference
### Props
| Prop | Type | Default | Description |
| ------------ | -------- | ------- | ----------------------------------------- |
| `speed` | `number` | `0.6` | Animation speed of the shader effect. |
| `repetition` | `number` | `4` | Number of pattern repetitions. |
| `softness` | `number` | `0.5` | Softness of the metallic highlights. |
| `shiftRed` | `number` | `0.3` | Red color shift amount. |
| `shiftBlue` | `number` | `0.3` | Blue color shift amount. |
| `distortion` | `number` | `0` | Distortion amount applied to the pattern. |
| `contour` | `number` | `0` | Contour intensity. |
| `angle` | `number` | `45` | Angle of the shader pattern in degrees. |
| `scale` | `number` | `8` | Scale of the shader pattern. |
| `offsetX` | `number` | `0.1` | Horizontal offset of the pattern. |
| `offsetY` | `number` | `-0.1` | Vertical offset of the pattern. |
# Mesh Gradient Card
---
title: Mesh Gradient Card
description: A card with an animated mesh gradient shader effect that cycles through configurable steps.
---
This card uses a WebGL mesh gradient shader from `@paper-design/shaders-react`
and animates between gradient steps using `motion`. Text is white by default
to contrast with the gradient background.
```tsx
import {
MeshGradientCard,
MeshGradientCardMessage,
MeshGradientCardMessages,
} from '@/components/ui/mesh-gradient-card';
const messages = [
'Painting with light.',
'Shifting dimensions.',
'Breathing color.',
'Tracing the unseen.',
'Folding horizons.',
'Dissolving edges.',
'Drifting through haze.',
];
export function MeshGradientCardDefault() {
return (
{messages.map((msg) => (
{msg}
))}
);
}
```
## Installation
```bash
npx shadcn@latest add @fab-ui/mesh-gradient-card
```
**Install the following dependencies:**
```bash
npm install @paper-design/shaders-react motion
```
**Copy and paste the following code into your project.**
```tsx
'use client';
import React, {
createContext,
useCallback,
useContext,
useEffect,
useRef,
useState,
} from 'react';
import {
MeshGradient,
type MeshGradientParams,
type MeshGradientProps,
} from '@paper-design/shaders-react';
import { animate, AnimatePresence, motion } from 'motion/react';
import { cn } from '@/lib/utils';
type GradientStep = Required<
Pick<
MeshGradientParams,
| 'distortion'
| 'swirl'
| 'grainMixer'
| 'grainOverlay'
| 'speed'
| 'scale'
| 'rotation'
| 'offsetX'
| 'offsetY'
>
>;
const DEFAULT_STEPS: GradientStep[] = [
{
distortion: 0.3,
swirl: 0.1,
grainMixer: 0.16,
grainOverlay: 0.15,
speed: 0.45,
scale: 1.0,
rotation: 0,
offsetX: 0,
offsetY: 0,
},
{
distortion: 0.5,
swirl: 0.3,
grainMixer: 0.1,
grainOverlay: 0.1,
speed: 0.55,
scale: 1.2,
rotation: 15,
offsetX: 0.1,
offsetY: -0.05,
},
{
distortion: 0.4,
swirl: 0.8,
grainMixer: 0.2,
grainOverlay: 0.1,
speed: 0.38,
scale: 0.9,
rotation: -10,
offsetX: -0.15,
offsetY: 0.1,
},
{
distortion: 0.9,
swirl: 0.5,
grainMixer: 0.12,
grainOverlay: 0.2,
speed: 0.65,
scale: 1.4,
rotation: 25,
offsetX: 0.05,
offsetY: -0.1,
},
{
distortion: 0.25,
swirl: 0.2,
grainMixer: 0.6,
grainOverlay: 0.5,
speed: 0.5,
scale: 1.1,
rotation: -5,
offsetX: -0.05,
offsetY: 0.05,
},
{
distortion: 0.6,
swirl: 0.9,
grainMixer: 0.08,
grainOverlay: 0.05,
speed: 0.3,
scale: 0.8,
rotation: 35,
offsetX: 0.12,
offsetY: 0.08,
},
{
distortion: 0.15,
swirl: 0.05,
grainMixer: 0.5,
grainOverlay: 0.4,
speed: 0.6,
scale: 1.3,
rotation: -20,
offsetX: -0.1,
offsetY: -0.08,
},
];
const MeshGradientCardContext = createContext<{ messageIndex: number }>({
messageIndex: 0,
});
type MeshGradientCardProps = Partial<
Omit
> & {
steps?: GradientStep[];
interval?: number;
children?: React.ReactNode;
};
function MeshGradientCardRoot({
className,
...props
}: React.ComponentProps<'div'>) {
return (
);
}
function MeshGradientCard({
className,
children,
colors = ['#fafafa', '#18181b', '#fafafa', '#18181b'],
steps = DEFAULT_STEPS,
interval = 4000,
...shaderProps
}: MeshGradientCardProps) {
const { config, messageIndex } = useGradientCycler(steps, interval);
return (
{children}
);
}
function MeshGradientCardMessages({
children,
className,
}: {
children?: React.ReactNode;
className?: string;
}) {
const { messageIndex } = useContext(MeshGradientCardContext);
const items = React.Children.toArray(children);
const active = items[messageIndex % items.length];
return (
{React.isValidElement(active)
? React.cloneElement(active, {
key: messageIndex,
} as React.Attributes)
: active}
);
}
function MeshGradientCardMessage({
children,
className,
}: {
children?: React.ReactNode;
className?: string;
}) {
return (
{children}
);
}
function useGradientCycler(
steps: GradientStep[] = DEFAULT_STEPS,
interval: number = 4000
) {
const [stepIndex, setStepIndex] = useState(0);
const [config, setConfig] = useState(steps[0]);
const animationRef = useRef | null>(null);
const [messageIndex, setMessageIndex] = useState(0);
const goToStep = useCallback(
(n: number) => {
if (n === stepIndex) return;
animationRef.current?.stop();
const from = { ...config };
const to = steps[n];
animationRef.current = animate(from, to, {
duration: 0.6,
ease: 'easeOut',
onUpdate: () => setConfig({ ...from }),
});
setStepIndex(n);
setMessageIndex((i) => i + 1);
},
[stepIndex, config, steps]
);
useEffect(() => {
const id = setTimeout(() => {
goToStep((stepIndex + 1) % steps.length);
}, interval);
return () => clearTimeout(id);
}, [stepIndex, goToStep, steps.length, interval]);
return { config, stepIndex, messageIndex, goToStep };
}
export {
MeshGradientCard,
MeshGradientCardMessage,
MeshGradientCardMessages,
MeshGradientCardRoot,
useGradientCycler,
};
export type { GradientStep, MeshGradientCardProps };
```
**Update the import paths to match your project setup.**
## Usage
```tsx
import {
MeshGradientCard,
MeshGradientCardMessage,
MeshGradientCardMessages,
} from "@/components/ui/mesh-gradient-card"
```
```tsx
const messages = ["First message.", "Second message.", "Third message."]
function Example() {
return (
{messages.map((msg) => (
{msg}
))}
)
}
```
## Examples
### Custom Colors
```tsx
import {
MeshGradientCard,
MeshGradientCardMessage,
MeshGradientCardMessages,
} from '@/components/ui/mesh-gradient-card';
const messages = [
'Forest calm.',
'Emerald glow.',
'Green pulse.',
'Leaf drift.',
'Deep canopy.',
'Jade shimmer.',
'Moss light.',
];
export function MeshGradientCardCustom() {
return (
{messages.map((msg) => (
{msg}
))}
);
}
```
### Custom Steps
```tsx
import {
MeshGradientCard,
MeshGradientCardMessage,
MeshGradientCardMessages,
type GradientStep,
} from '@/components/ui/mesh-gradient-card';
const steps: GradientStep[] = [
{
distortion: 0.8,
swirl: 0.1,
grainMixer: 0.2,
grainOverlay: 0.15,
speed: 0.7,
scale: 1.3,
rotation: 10,
offsetX: 0.1,
offsetY: -0.05,
},
{
distortion: 0.1,
swirl: 0.9,
grainMixer: 0.05,
grainOverlay: 0.08,
speed: 0.4,
scale: 0.85,
rotation: -15,
offsetX: -0.1,
offsetY: 0.1,
},
{
distortion: 0.45,
swirl: 0.45,
grainMixer: 0.12,
grainOverlay: 0.12,
speed: 0.55,
scale: 1.1,
rotation: 20,
offsetX: 0.05,
offsetY: 0.05,
},
];
const messages = ['High distortion.', 'Deep swirl.', 'Balanced flow.'];
export function MeshGradientCardCustomSteps() {
return (
{messages.map((msg) => (
{msg}
))}
);
}
```
## API Reference
### Props
| Prop | Type | Default | Description |
| -------------- | ---------------- | ---------------------------------------------- | ------------------------------------------------ |
| `colors` | `string[]` | `['#fafafa', '#18181b', '#fafafa', '#18181b']` | Array of colors for the mesh gradient. |
| `steps` | `GradientStep[]` | 7 built-in steps | Array of gradient step configurations to cycle. |
| `interval` | `number` | `4000` | Time in ms between gradient step transitions. |
| `distortion` | `number` | per step | Distortion amount applied to the gradient. |
| `swirl` | `number` | per step | Swirl intensity of the gradient. |
| `grainMixer` | `number` | per step | Grain mixer intensity. |
| `grainOverlay` | `number` | per step | Grain overlay intensity. |
| `speed` | `number` | per step | Animation speed of the shader effect. |
| `scale` | `number` | per step | Scale of the gradient pattern. |
| `rotation` | `number` | per step | Rotation angle of the gradient in degrees. |
| `offsetX` | `number` | per step | Horizontal offset of the gradient. |
| `offsetY` | `number` | per step | Vertical offset of the gradient. |