# 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. Cancel Continue ); } ``` ## 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 ( ; } ``` ## 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 (
Photo by mymind on Unsplash 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 allow Allow 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 (
img:first-child]:pt-0 data-[size=sm]:gap-4 data-[size=sm]:py-4 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl', className )} {...props} /> ); } function CardHeader({ className, ...props }: React.ComponentProps<'div'>) { return (
); } function CardTitle({ className, ...props }: React.ComponentProps<'div'>) { return (
); } function CardDescription({ className, ...props }: React.ComponentProps<'div'>) { return (
); } function CardAction({ className, ...props }: React.ComponentProps<'div'>) { return (
); } function CardContent({ className, ...props }: React.ComponentProps<'div'>) { return (
); } function CardFooter({ className, ...props }: React.ComponentProps<'div'>) { return (
); } export { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent, }; ``` **Update the import paths to match your project setup.** ## Usage ```tsx showLineNumbers import { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, } from "@/components/ui/card" ``` ```tsx showLineNumbers Card Title Card Description Card Action

Card Content

Card Footer

``` # 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 Back Forward Reload Save 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 here Back Forward Save 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 Tab New Window More Tools Save Page As... Create Shortcut... Developer Tools Settings ); } ``` # 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 ( }> Open Dialog Edit Profile Make changes to your profile here. Click save when you're done.

Your profile settings will be updated.

); } ``` ## 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 Open Dialog Title Dialog description goes here. ``` ## 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 ( }> Open Dialog Confirm Action Are you sure you want to proceed with this action? }> Cancel ); } ``` ### 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 ( }> Terms of Service Terms of Service Please read these terms carefully before using our service.

1. Acceptance of Terms

By accessing and using this service, you accept and agree to be bound by the terms and provisions of this agreement.

2. Use License

Permission is granted to temporarily use this service for personal, non-commercial purposes only. This is the grant of a license, not a transfer of title.

3. User Account

You are responsible for maintaining the confidentiality of your account and password. You agree to accept responsibility for all activities that occur under your account.

4. Privacy Policy

Your use of this service is also governed by our Privacy Policy. Please review our Privacy Policy, which also governs the site and informs users of our data collection practices.

5. Limitation of Liability

In no event shall we be liable for any damages arising out of the use or inability to use the materials on this service, even if we have been notified of the possibility of such damage.

6. Modifications

We may revise these terms of service at any time without notice. By using this service you are agreeing to be bound by the then current version of these terms.

); } ``` # 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 Open Drawer Title Drawer 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.