Empty
A placeholder component for empty states, with optional icon, title, description, and content slots.
Preview
import { PlusIcon, StackIcon } from "@radix-ui/react-icons";
import { Button } from "@/components/ui/Button";
import {
Empty,
EmptyHeader,
EmptyTitle,
EmptyDescription,
EmptyContent,
EmptyMedia,
} from "@/components/ui/Empty";
export function EmptyDemo() {
return {
return (
<Empty className="min-h-32">
<EmptyHeader>
<EmptyMedia variant="icon">
<StackIcon className="icon h4" aria-hidden />
</EmptyMedia>
<EmptyTitle>No items yet</EmptyTitle>
<EmptyDescription>
Get started by adding your first item. You can always change this
later.
</EmptyDescription>
</EmptyHeader>
</Empty>
);
};Installation
Make sure that namespace is set in your component.json file. Namespace docs: Learn more about namespaces
pnpm dlx shadcn@latest add @aura/emptyManual
Install the following dependencies:
pnpm install class-variance-authorityCopy and paste the class names utility into your utils/class-names.ts file.
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
Copy and paste the Empty component into your components/ui/Empty.tsx file.
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/utils/class-names"
function Empty({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="empty"
className={cn(
"flex min-w-0 flex-1 flex-col items-center justify-center gap-1 rounded-lg border border-dashed border-gray-6 bg-gray-2 p-2 text-balance text-center text-gray-12",
className
)}
{...props}
/>
)
}
function EmptyHeader({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="empty-header"
className={cn(
"flex max-w-sm flex-col items-center gap-1 text-center",
className
)}
{...props}
/>
)
}
const emptyMediaVariants = cva(
"mb-0.5 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0",
{
variants: {
variant: {
default: "bg-transparent",
icon: "flex size-5 shrink-0 items-center justify-center rounded-lg bg-gray-3 text-gray-12 [&_svg:not([class*='size-'])]:size-4",
},
},
defaultVariants: {
variant: "default",
},
}
)
function EmptyMedia({
className,
variant = "default",
...props
}: React.ComponentProps<"div"> & VariantProps<typeof emptyMediaVariants>) {
return (
<div
data-slot="empty-icon"
data-variant={variant}
className={cn(emptyMediaVariants({ variant, className }))}
{...props}
/>
)
}
function EmptyTitle({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="empty-title"
className={cn("h5 font-medium tracking-tight text-gray-12", className)}
{...props}
/>
)
}
function EmptyDescription({ className, ...props }: React.ComponentProps<"p">) {
return (
<p
data-slot="empty-description"
className={cn(
"text-sm/relaxed text-gray-11 [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary",
className
)}
{...props}
/>
)
}
function EmptyContent({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="empty-content"
className={cn(
"flex w-full min-w-0 max-w-sm flex-col items-center gap-1 text-balance text-sm text-gray-12",
className
)}
{...props}
/>
)
}
export {
Empty,
EmptyHeader,
EmptyTitle,
EmptyDescription,
EmptyContent,
EmptyMedia,
}Usage
WithActions
export const WithActions = () => {
return (
<Empty className="min-h-40">
<EmptyHeader>
<EmptyMedia variant="icon">
<StackIcon className="icon h4" aria-hidden />
</EmptyMedia>
<EmptyTitle>Nothing in this list</EmptyTitle>
<EmptyDescription>
Create a record to see it here, or{" "}
<a href="#browse">browse existing items</a>.
</EmptyDescription>
</EmptyHeader>
<EmptyContent>
<div className="flex flex-wrap items-center justify-center gap-1">
<Button variant="fill" type="button" className="gap-1">
<PlusIcon className="icon" aria-hidden />
Add item
</Button>
<Button variant="pill" type="button">
Learn more
</Button>
</div>
</EmptyContent>
</Empty>
);
};Dropdown Menu
Displays a menu of actions or options triggered by a button.
Form
A comprehensive form system with schema-based validation, error handling, and field management. Built with Radix UI primitives and AJV validation, supporting text inputs, textareas, selects, checkboxes, switches, and checkbox groups with automatic error propagation and touch state management.