Trống
Một biểu tượng đại diện cho các kết quả tìm kiếm trống, bảng điều khiển trống và màn hình khởi động lần đầu — bao gồm biểu tượng, tiêu đề và hành động được bao quanh bởi các đường viền theo phong cách tân thô mộc.
import { IconFolderCode } from "@tabler/icons-react"
import { ArrowUpRightIcon } from "lucide-react"
Trạng thái rỗng lấp vào chỗ trống khi chẳng có gì để hiển thị — một chỗ dành cho biểu tượng hoặc ảnh đại diện, một tiêu đề, một dòng mô tả và một lời kêu gọi hành động. Đây là một bộ các thẻ div ghép được với nhau, không phụ thuộc thư viện nào (bên dưới không có primitive), tạo kiểu theo công thức neobrutalist: viền dày và kiểu chữ tiêu đề đậm.
Nên dùng khi:
- Tìm kiếm và bộ lọc không ra kết quả — nói rõ vì sao danh sách trống và mở đường để đặt lại hoặc nới rộng truy vấn.
- Màn hình dùng lần đầu — “Chưa có dự án nào” kèm một nút tạo mới chỉ thẳng vào bước tiếp theo.
- Hộp thư và hàng đợi đã xử lý xong — cho người dùng biết danh sách trống là dấu hiệu thành công, không phải lỗi.
Cài đặt#
pnpm dlx shadcn@latest add https://neobrutalism.com/r/base/empty.jsonnpx shadcn@latest add https://neobrutalism.com/r/base/empty.jsonyarn dlx shadcn@latest add https://neobrutalism.com/r/base/empty.jsonbunx --bun shadcn@latest add https://neobrutalism.com/r/base/empty.json
Sao chép và dán đoạn mã sau vào dự án của bạn.
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 w-full min-w-0 flex-1 flex-col items-center justify-center gap-4 rounded border-2 bg-card p-6 text-center text-balance",
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", className)}
{...props}
/>
)
}
const emptyMediaVariants = cva(
"mb-2 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0",
{
variants: {
variant: {
default: "bg-transparent",
icon: "flex size-8 shrink-0 items-center justify-center rounded-lg bg-muted text-foreground [&_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(
"cn-font-heading text-sm font-medium tracking-tight",
className
)}
{...props}
/>
)
}
function EmptyDescription({ className, ...props }: React.ComponentProps<"p">) {
return (
<div
data-slot="empty-description"
className={cn(
"text-sm/relaxed text-muted-foreground [&>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 max-w-sm min-w-0 flex-col items-center gap-2.5 text-sm text-balance",
className
)}
{...props}
/>
)
}
export {
Empty,
EmptyHeader,
EmptyTitle,
EmptyDescription,
EmptyContent,
EmptyMedia,
}
Cập nhật các đường dẫn import cho phù hợp với thiết lập dự án của bạn.
Cách dùng#
import {
Empty,
EmptyContent,
EmptyDescription,
EmptyHeader,
EmptyMedia,
EmptyTitle,
} from "@/components/ui/empty"<Empty>
<EmptyHeader>
<EmptyMedia variant="icon">
<Icon />
</EmptyMedia>
<EmptyTitle>No data</EmptyTitle>
<EmptyDescription>No data found</EmptyDescription>
</EmptyHeader>
<EmptyContent>
<Button>Add data</Button>
</EmptyContent>
</Empty>Kết hợp#
Dùng cách kết hợp sau để xây dựng một trạng thái Empty:
Empty
├── EmptyHeader
│ ├── EmptyMedia
│ ├── EmptyTitle
│ └── EmptyDescription
└── EmptyContentVí dụ#
Viền#
Dùng lớp tiện ích border để tạo một trạng thái trống có viền.
import { IconCloud } from "@tabler/icons-react"
import { Button } from "@/components/ui/button"Nền#
Dùng các tiện ích bg-* và bg-gradient-* để thêm nền cho trạng thái trống.
import { IconBell } from "@tabler/icons-react"
import { RefreshCcwIcon } from "lucide-react"
Avatar#
Dùng thành phần EmptyMedia để hiển thị một avatar trong trạng thái trống.
import {
Avatar,
AvatarFallback,Nhóm avatar#
Dùng thành phần EmptyMedia để hiển thị một nhóm avatar trong trạng thái trống.
import { PlusIcon } from "lucide-react"
import {InputGroup#
Bạn có thể thêm một thành phần InputGroup vào thành phần EmptyContent.
import { SearchIcon } from "lucide-react"
import {RTL#
Để bật hỗ trợ RTL trong Neobrutalism, hãy xem hướng dẫn cấu hình RTL.
"use client"
import * as React from "react"Khả năng truy cập#
Trạng thái rỗng chỉ mang tính trình bày — chỉ là các thẻ div thuần, không có role hay hành vi bàn phím nào, nên chẳng có gì phải kết nối thêm. Hãy đánh dấu biểu tượng EmptyMedia thuần trang trí bằng aria-hidden="true", và giữ các hành động trong EmptyContent ở dạng Button hoặc liên kết thật để chúng vẫn nhận được focus. Khi trạng thái rỗng thay thế nội dung sau một tác vụ bất đồng bộ (một lượt tìm kiếm trả về không kết quả), hãy thông báo qua live region — bản thân việc thay đổi DOM là hoàn toàn im lặng đối với trình đọc màn hình.
Tham chiếu API#
Empty#
Thành phần chính của trạng thái trống. Bao bọc các thành phần EmptyHeader và EmptyContent.
| Prop | Type | Default |
|---|---|---|
className | string |
<Empty>
<EmptyHeader />
<EmptyContent />
</Empty>EmptyHeader#
Thành phần EmptyHeader bao bọc phần media, tiêu đề và mô tả của trạng thái trống.
| Prop | Type | Default |
|---|---|---|
className | string |
<EmptyHeader>
<EmptyMedia />
<EmptyTitle />
<EmptyDescription />
</EmptyHeader>EmptyMedia#
Dùng thành phần EmptyMedia để hiển thị phần media của trạng thái trống, chẳng hạn như biểu tượng hoặc hình ảnh. Bạn cũng có thể dùng nó để hiển thị các thành phần khác như avatar.
| Prop | Type | Default |
|---|---|---|
variant | "default" | "icon" | default |
className | string |
<EmptyMedia variant="icon">
<Icon />
</EmptyMedia><EmptyMedia>
<Avatar>
<AvatarImage src="..." />
<AvatarFallback>CN</AvatarFallback>
</Avatar>
</EmptyMedia>EmptyTitle#
Dùng thành phần EmptyTitle để hiển thị tiêu đề của trạng thái trống.
| Prop | Type | Default |
|---|---|---|
className | string |
<EmptyTitle>No data</EmptyTitle>EmptyDescription#
Dùng thành phần EmptyDescription để hiển thị mô tả của trạng thái trống.
| Prop | Type | Default |
|---|---|---|
className | string |
<EmptyDescription>You do not have any notifications.</EmptyDescription>EmptyContent#
Dùng thành phần EmptyContent để hiển thị nội dung của trạng thái trống, chẳng hạn như một nút, ô nhập hoặc một liên kết.
| Prop | Type | Default |
|---|---|---|
className | string |
<EmptyContent>
<Button>Add Project</Button>
</EmptyContent>