Vide
Élément de remplacement pour les recherches sans résultat, les tableaux de bord vides et les écrans de premier lancement : icône, titre et action encadrés par des bordures de style néobrutaliste.
import { IconFolderCode } from "@tabler/icons-react"
import { ArrowUpRightIcon } from "lucide-react"
L’état vide comble le manque quand il n’y a rien à afficher — un emplacement pour une icône ou un avatar, un titre, une description et un appel à l’action. C’est un ensemble de div composables et sans dépendance (aucune primitive sous-jacente), habillé selon la recette néobrutaliste : bordures épaisses et titres à la typographie affirmée.
À privilégier pour :
- Recherches et filtres sans résultat — expliquez pourquoi la liste est vide et proposez un moyen de réinitialiser ou d’élargir la requête.
- Écrans de première utilisation — « Aucun projet pour l’instant » avec un bouton de création pointant vers la toute prochaine étape.
- Boîtes de réception et files d’attente vidées — confirmez que la liste vide est une réussite, pas une erreur.
Installation#
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
Copiez-collez le code suivant dans votre projet.
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,
}
Adaptez les chemins d’import à la structure de votre projet.
Utilisation#
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>Composition#
Utilisez la composition suivante pour construire un état Empty :
Empty
├── EmptyHeader
│ ├── EmptyMedia
│ ├── EmptyTitle
│ └── EmptyDescription
└── EmptyContentExemples#
Contour#
Utilisez la classe utilitaire border pour créer un état vide avec contour.
import { IconCloud } from "@tabler/icons-react"
import { Button } from "@/components/ui/button"Arrière-plan#
Utilisez les utilitaires bg-* et bg-gradient-* pour ajouter un arrière-plan à l’état vide.
import { IconBell } from "@tabler/icons-react"
import { RefreshCcwIcon } from "lucide-react"
Avatar#
Utilisez le composant EmptyMedia pour afficher un avatar dans l’état vide.
import {
Avatar,
AvatarFallback,Groupe d’avatars#
Utilisez le composant EmptyMedia pour afficher un groupe d’avatars dans l’état vide.
import { PlusIcon } from "lucide-react"
import {InputGroup#
Vous pouvez ajouter un composant InputGroup au composant EmptyContent.
import { SearchIcon } from "lucide-react"
import {RTL#
Pour activer le RTL dans Neobrutalism, consultez le guide de configuration RTL.
"use client"
import * as React from "react"Accessibilité#
L’état vide est purement présentationnel — de simples div sans rôle ni comportement clavier, il n’y a donc rien à câbler. Marquez une icône EmptyMedia purement décorative avec aria-hidden="true", et gardez les actions de EmptyContent sous forme de vrais Button ou de vrais liens afin qu’elles restent focalisables. Quand l’état vide remplace du contenu après une opération asynchrone (une recherche qui revient sans résultat), annoncez-le via une région live — le remplacement dans le DOM est en soi silencieux pour les lecteurs d’écran.
Référence API#
Empty#
Le composant principal de l’état vide. Il enveloppe les composants EmptyHeader et EmptyContent.
| Prop | Type | Default |
|---|---|---|
className | string |
<Empty>
<EmptyHeader />
<EmptyContent />
</Empty>EmptyHeader#
Le composant EmptyHeader enveloppe le média, le titre et la description de l’état vide.
| Prop | Type | Default |
|---|---|---|
className | string |
<EmptyHeader>
<EmptyMedia />
<EmptyTitle />
<EmptyDescription />
</EmptyHeader>EmptyMedia#
Utilisez le composant EmptyMedia pour afficher le média de l’état vide, comme une icône ou une image. Vous pouvez aussi l’utiliser pour afficher d’autres composants, comme un 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#
Utilisez le composant EmptyTitle pour afficher le titre de l’état vide.
| Prop | Type | Default |
|---|---|---|
className | string |
<EmptyTitle>No data</EmptyTitle>EmptyDescription#
Utilisez le composant EmptyDescription pour afficher la description de l’état vide.
| Prop | Type | Default |
|---|---|---|
className | string |
<EmptyDescription>You do not have any notifications.</EmptyDescription>EmptyContent#
Utilisez le composant EmptyContent pour afficher le contenu de l’état vide, comme un bouton, un champ de saisie ou un lien.
| Prop | Type | Default |
|---|---|---|
className | string |
<EmptyContent>
<Button>Add Project</Button>
</EmptyContent>