Lewati ke konten

Kosong

Tempat penampung untuk hasil pencarian kosong, dasbor kosong, dan layar saat pertama kali dijalankan — ikon, judul, dan tindakan yang dibingkai dengan garis tepi bergaya neobrutalis.

import { IconFolderCode } from "@tabler/icons-react"
import { ArrowUpRightIcon } from "lucide-react"

Empty state mengisi kekosongan saat tidak ada yang bisa dirender — slot untuk ikon atau avatar, judul, deskripsi, dan ajakan bertindak. Komponen ini berupa kumpulan div yang dapat dikomposisikan tanpa dependensi (tanpa primitif di baliknya), ditata dengan resep neobrutalis: garis tepi tebal dan judul bertipografi tegas.

Gunakan komponen ini saat:

  • Pencarian dan filter tanpa hasil — jelaskan mengapa daftar kosong dan tawarkan cara mengatur ulang atau memperluas kueri.
  • Layar pemakaian pertama — "Belum ada proyek" dengan tombol buat yang menunjuk langsung ke langkah berikutnya.
  • Kotak masuk dan antrean yang sudah bersih — tegaskan bahwa daftar kosong adalah keberhasilan, bukan kesalahan.

Instalasi

pnpm dlx shadcn@latest add https://neobrutalism.com/r/base/empty.json
npx shadcn@latest add https://neobrutalism.com/r/base/empty.json
yarn dlx shadcn@latest add https://neobrutalism.com/r/base/empty.json
bunx --bun shadcn@latest add https://neobrutalism.com/r/base/empty.json

Salin dan tempel kode berikut ke dalam proyek Anda.

components/ui/empty.tsx
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,
}

Perbarui jalur impor agar sesuai dengan pengaturan proyek Anda.

Penggunaan

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>

Komposisi

Gunakan komposisi berikut untuk membangun keadaan Empty:

Empty
├── EmptyHeader
│   ├── EmptyMedia
│   ├── EmptyTitle
│   └── EmptyDescription
└── EmptyContent

Contoh

Garis Luar

Gunakan kelas utilitas border untuk membuat keadaan kosong dengan garis luar.

import { IconCloud } from "@tabler/icons-react"

import { Button } from "@/components/ui/button"

Latar Belakang

Gunakan utilitas bg-* dan bg-gradient-* untuk menambahkan latar belakang ke keadaan kosong.

import { IconBell } from "@tabler/icons-react"
import { RefreshCcwIcon } from "lucide-react"

Avatar

Gunakan komponen EmptyMedia untuk menampilkan avatar dalam keadaan kosong.

import {
  Avatar,
  AvatarFallback,

Grup Avatar

Gunakan komponen EmptyMedia untuk menampilkan grup avatar dalam keadaan kosong.

import { PlusIcon } from "lucide-react"

import {

InputGroup

Anda dapat menambahkan komponen InputGroup ke komponen EmptyContent.

import { SearchIcon } from "lucide-react"

import {

RTL

Untuk mengaktifkan dukungan RTL di Neobrutalism, lihat panduan konfigurasi RTL.

"use client"

import * as React from "react"

Aksesibilitas

Empty state bersifat presentasional — div biasa tanpa role atau perilaku keyboard, jadi tidak ada yang perlu disambungkan. Tandai ikon EmptyMedia yang murni dekoratif dengan aria-hidden="true", dan pastikan aksi di dalam EmptyContent berupa elemen Button atau tautan sungguhan agar tetap dapat difokus. Jika empty state menggantikan konten setelah operasi asinkron (pencarian yang kembali tanpa hasil), umumkan lewat live region — pergantian DOM itu sendiri tidak terdengar oleh pembaca layar.

Referensi API

Empty

Komponen utama dari keadaan kosong. Membungkus komponen EmptyHeader dan EmptyContent.

PropTypeDefault
classNamestring
<Empty>
  <EmptyHeader />
  <EmptyContent />
</Empty>

EmptyHeader

Komponen EmptyHeader membungkus media, judul, dan deskripsi keadaan kosong.

PropTypeDefault
classNamestring
<EmptyHeader>
  <EmptyMedia />
  <EmptyTitle />
  <EmptyDescription />
</EmptyHeader>

EmptyMedia

Gunakan komponen EmptyMedia untuk menampilkan media keadaan kosong seperti ikon atau gambar. Anda juga dapat menggunakannya untuk menampilkan komponen lain seperti avatar.

PropTypeDefault
variant"default" | "icon"default
classNamestring
<EmptyMedia variant="icon">
  <Icon />
</EmptyMedia>
<EmptyMedia>
  <Avatar>
    <AvatarImage src="..." />
    <AvatarFallback>CN</AvatarFallback>
  </Avatar>
</EmptyMedia>

EmptyTitle

Gunakan komponen EmptyTitle untuk menampilkan judul keadaan kosong.

PropTypeDefault
classNamestring
<EmptyTitle>No data</EmptyTitle>

EmptyDescription

Gunakan komponen EmptyDescription untuk menampilkan deskripsi keadaan kosong.

PropTypeDefault
classNamestring
<EmptyDescription>You do not have any notifications.</EmptyDescription>

EmptyContent

Gunakan komponen EmptyContent untuk menampilkan konten keadaan kosong seperti tombol, input, atau tautan.

PropTypeDefault
classNamestring
<EmptyContent>
  <Button>Add Project</Button>
</EmptyContent>