Anatomy
- Page header — title, description, primary action
- Filter bar — search, category filters, sort
- Content — a table or a card grid
- Pagination
- Empty state — for when nothing matches
Documentation Index
Fetch the complete documentation index at: /llms.txt
Use this file to discover all available pages before exploring further.
List and table pages — filtering, sorting, pagination, loading skeletons and an empty state that gives the reader something to do.
<div className="mx-auto max-w-6xl px-4 py-8 sm:px-6">
<div className="flex items-center justify-between">
<div>
<h1 className="font-serif text-2xl font-bold">Events</h1>
<p className="text-sm text-muted-foreground">Browse upcoming events</p>
</div>
<Button>Create event</Button>
</div>
<FilterBar className="mt-6" />
<div className="mt-6">
<DataTable columns={columns} data={events} />
</div>
<Pagination className="mt-6" />
</div>
| Use a table when | Use a card grid when |
|---|---|
| Items have many comparable fields | Items are visually distinct |
| People scan and compare | Items carry images or rich content |
| Sorting by column matters | The mobile layout needs flexibility |
| Data density is high | Content lengths vary |
<DataTable
columns={[
{ accessorKey: "name", header: "Name" },
{ accessorKey: "date", header: "Date" },
{ accessorKey: "status", header: "Status" },
]}
data={items}
/>
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
{items.map((item) => (
<Card key={item.id}>
<CardHeader><CardTitle>{item.name}</CardTitle></CardHeader>
<CardContent>
<p className="text-sm text-muted-foreground">{item.description}</p>
</CardContent>
</Card>
))}
</div>
<div className="flex flex-col gap-4 sm:flex-row">
<SearchBar placeholder="Search events…" onSearch={handleSearch} className="flex-1" />
<FilterBar
filters={[
{ key: "category", label: "Category", options: categories },
{ key: "status", label: "Status", options: statuses },
]}
onFilterChange={handleFilterChange}
/>
</div>
npx shadcn@latest add \
https://mzizi.dev/api/v1/ui/filter-bar \
https://mzizi.dev/api/v1/ui/search-bar \
https://mzizi.dev/api/v1/ui/data-table \
https://mzizi.dev/api/v1/ui/empty
<Empty>
<EmptyHeader>
<EmptyMedia>
<Search className="size-12 text-muted-foreground" />
</EmptyMedia>
<EmptyTitle>No events found</EmptyTitle>
<EmptyDescription>
Try adjusting your filters or create a new event.
</EmptyDescription>
</EmptyHeader>
<EmptyContent>
<Button>Create event</Button>
</EmptyContent>
</Empty>
{/* Table */}
<div className="space-y-3">
{Array.from({ length: 5 }).map((_, i) => (
<Skeleton key={i} className="h-12 w-full" />
))}
</div>
{/* Card grid */}
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
{Array.from({ length: 6 }).map((_, i) => (
<Skeleton key={i} className="h-48 w-full rounded-lg" />
))}
</div>