Empty
A simplified wrapper for Shadcn Empty with flexible option handling and automatic type conversion
No Messages
You haven't received any messages yet. When someone contacts you, they'll show up here.
Installation
npx shadcn@latest add @glrk-ui/emptyIf you haven't set up the prerequisites yet, check out Prerequest section.
Copy and paste the following code into shadcn empty component.
type EmptyWrapperProps = {
title?: React.ReactNode
description?: React.ReactNode
media?: React.ReactNode
content?: React.ReactNode
wrapperCls?: string
headerCls?: string
titleCls?: string
mediaCls?: string
descriptionCls?: string
contentCls?: string
mediaVariant?: VariantProps<typeof emptyMediaVariants>["variant"]
}
function EmptyWrapper({
title,
description,
media,
content,
wrapperCls,
headerCls,
titleCls,
mediaCls,
descriptionCls,
contentCls,
mediaVariant = "default",
}: EmptyWrapperProps) {
return (
<Empty className={wrapperCls}>
<EmptyHeader className={headerCls}>
{media && <EmptyMedia className={mediaCls} variant={mediaVariant}>{media}</EmptyMedia>}
{title && <EmptyTitle className={titleCls}>{title}</EmptyTitle>}
{description && <EmptyDescription className={descriptionCls}>{description}</EmptyDescription>}
</EmptyHeader>
{
content &&
<EmptyContent className={contentCls}>
{content}
</EmptyContent>
}
</Empty>
)
}export {
Empty,
EmptyHeader,
EmptyTitle,
EmptyDescription,
EmptyContent,
EmptyMedia,
EmptyWrapper,
}empty.tsx
Copy and paste the following code into your project.
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
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-6 rounded-lg border-dashed p-6 text-center text-balance md:p-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-2 text-center",
className
)}
{...props}
/>
)
}
const emptyMediaVariants = cva(
"flex shrink-0 items-center justify-center mb-2 [&_svg]:pointer-events-none [&_svg]:shrink-0",
{
variants: {
variant: {
default: "bg-transparent",
icon: "bg-muted text-foreground flex size-10 shrink-0 items-center justify-center rounded-lg [&_svg:not([class*='size-'])]:size-6",
},
},
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("text-lg font-medium tracking-tight", className)}
{...props}
/>
)
}
function EmptyDescription({ className, ...props }: React.ComponentProps<"p">) {
return (
<div
data-slot="empty-description"
className={cn(
"text-muted-foreground [&>a:hover]:text-primary text-sm/relaxed [&>a]:underline [&>a]:underline-offset-4",
className
)}
{...props}
/>
)
}
function EmptyContent({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="empty-content"
className={cn(
"flex w-full max-w-sm min-w-0 flex-col items-center gap-4 text-sm text-balance",
className
)}
{...props}
/>
)
}
type EmptyWrapperProps = {
title?: React.ReactNode
description?: React.ReactNode
media?: React.ReactNode
content?: React.ReactNode
wrapperCls?: string
headerCls?: string
titleCls?: string
mediaCls?: string
descriptionCls?: string
contentCls?: string
mediaVariant?: VariantProps<typeof emptyMediaVariants>["variant"]
}
function EmptyWrapper({
title,
description,
media,
content,
wrapperCls,
headerCls,
titleCls,
mediaCls,
descriptionCls,
contentCls,
mediaVariant = "default",
}: EmptyWrapperProps) {
return (
<Empty className={wrapperCls}>
<EmptyHeader className={headerCls}>
{media && <EmptyMedia className={mediaCls} variant={mediaVariant}>{media}</EmptyMedia>}
{title && <EmptyTitle className={titleCls}>{title}</EmptyTitle>}
{description && <EmptyDescription className={descriptionCls}>{description}</EmptyDescription>}
</EmptyHeader>
{
content &&
<EmptyContent className={contentCls}>
{content}
</EmptyContent>
}
</Empty>
)
}
export {
Empty,
EmptyHeader,
EmptyTitle,
EmptyDescription,
EmptyContent,
EmptyMedia,
EmptyWrapper,
}Done
You can now use EmptyWrapper
Usage
Basic
import { Inbox } from "lucide-react"
import { EmptyWrapper } from "@/components/ui/empty"
import { Button } from "@/components/ui/button"
export function Basic() {
return (
<EmptyWrapper
title="No Messages"
description="You haven't received any messages yet. When someone contacts you, they'll show up here."
media={<Inbox className="size-10 text-muted-foreground" />}
content={<Button>Compose Message</Button>}
/>
)
}Custom Styling
import { EmptyWrapper } from "@/components/ui/empty";
import { AlertCircle } from "lucide-react";
export function CustomStyling() {
return (
<EmptyWrapper
media={<AlertCircle />}
mediaVariant="icon"
title="No results found"
description="Try adjusting your search or filter to find what you're looking for"
wrapperCls="border-2 bg-muted/50"
titleCls="text-destructive"
mediaCls="bg-destructive/10"
/>
)
}Reference
EmptyWrapper
Prop
Type