Naar inhoud

Leeg

Een plaatshouder voor zoekopdrachten zonder resultaten, lege dashboards en startschermen — pictogram, titel en actie omlijst door neobrutalistische randen.

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

De empty state vult het gat wanneer er niets te tonen is — een slot voor een icoon of avatar, een titel, een beschrijving en een call-to-action. Het is een set composeerbare divs zonder dependencies (geen onderliggende primitive), vormgegeven volgens het neobrutalistische recept: dikke randen en vette koppen.

Ideaal voor:

  • Zoekopdrachten en filters zonder resultaten — vertel waarom de lijst leeg is en bied een manier om te resetten of de zoekopdracht te verbreden.
  • First-run-schermen — ‘Nog geen projecten’ met een aanmaakknop die naar de eerstvolgende stap wijst.
  • Leeggemaakte inboxen en wachtrijen — bevestig dat de lege lijst een succes is, geen fout.

Installatie

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

Kopieer en plak de volgende code in je project.

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,
}

Werk de importpaden bij zodat ze overeenkomen met je projectconfiguratie.

Gebruik

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>

Compositie

Gebruik de volgende compositie om een Empty-staat te bouwen:

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

Voorbeelden

Omtrek

Gebruik de utility-klasse border om een lege staat met omtrek te maken.

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

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

Achtergrond

Gebruik de utilities bg-* en bg-gradient-* om een achtergrond toe te voegen aan de lege staat.

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

Avatar

Gebruik het EmptyMedia-component om een avatar in de lege staat te tonen.

import {
  Avatar,
  AvatarFallback,

Avatargroep

Gebruik het EmptyMedia-component om een avatargroep in de lege staat te tonen.

import { PlusIcon } from "lucide-react"

import {

InputGroup

Je kunt een InputGroup-component toevoegen aan het EmptyContent-component.

import { SearchIcon } from "lucide-react"

import {

RTL

Om RTL-ondersteuning in Neobrutalism in te schakelen, zie de RTL-configuratiegids.

"use client"

import * as React from "react"

Toegankelijkheid

De empty state is presentationeel — gewone divs zonder rollen of toetsenbordgedrag, dus er valt niets aan te sluiten. Markeer een puur decoratief EmptyMedia-icoon met aria-hidden="true" en houd acties in EmptyContent echte Button- of linkelementen, zodat ze focusbaar blijven. Wanneer de empty state content vervangt na een asynchrone bewerking (een zoekopdracht die zonder resultaten terugkomt), kondig dat dan aan via een live region — de DOM-wissel zelf blijft voor schermlezers onopgemerkt.

API-referentie

Empty

Het hoofdcomponent van de lege staat. Omhult de componenten EmptyHeader en EmptyContent.

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

EmptyHeader

Het EmptyHeader-component omhult de media, titel en beschrijving van de lege staat.

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

EmptyMedia

Gebruik het EmptyMedia-component om de media van de lege staat te tonen, zoals een pictogram of een afbeelding. Je kunt het ook gebruiken om andere componenten te tonen, zoals een avatar.

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

EmptyTitle

Gebruik het EmptyTitle-component om de titel van de lege staat te tonen.

PropTypeDefault
classNamestring
<EmptyTitle>No data</EmptyTitle>

EmptyDescription

Gebruik het EmptyDescription-component om de beschrijving van de lege staat te tonen.

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

EmptyContent

Gebruik het EmptyContent-component om de inhoud van de lege staat te tonen, zoals een knop, invoerveld of een link.

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