Data Display
Tables, badges, avatars, and data presentation components.
Display data with Table for basic layouts or DataTable for sorting, filtering, and pagination via TanStack Table. Use Badge for status labels, Avatar/ProfileAvatar for user images, EmptyState for zero-data screens, Skeleton for loading placeholders, and Chart for visualizations with Recharts.
This guide is part of the UI Components documentation.
Data display components present information in structured, scannable formats - from tabular data and status indicators to user avatars and loading states - with consistent styling and accessibility.
Use Table when: displaying simple, static data without sorting/filtering.
- Use DataTable when: you need pagination, sorting, or filtering (most admin views).
- Use Badge when: showing status (active/inactive) or categories.
- Use Skeleton when: loading takes >300ms.
- If unsure: DataTable for lists, Badge for status.
Table
Basic table for structured data.
import { Table, TableHeader, TableBody, TableRow, TableHead, TableCell } from '@kit/ui/table';<Table> <TableHeader> <TableRow> <TableHead>Name</TableHead> <TableHead>Email</TableHead> <TableHead>Status</TableHead> </TableRow> </TableHeader> <TableBody> <TableRow> <TableCell>John Doe</TableCell> <TableCell>john@example.com</TableCell> <TableCell>Active</TableCell> </TableRow> </TableBody></Table>Data Table
Advanced table with sorting, filtering, and pagination using TanStack Table.
import { DataTable } from '@kit/ui/data-table';import { ColumnDef } from '@tanstack/react-table';const columns: ColumnDef<User>[] = [ { accessorKey: 'name', header: 'Name', }, { accessorKey: 'email', header: 'Email', }, { accessorKey: 'status', header: 'Status', },];<DataTable data={users} columns={columns} pageIndex={0} pageSize={10} pageCount={totalPages}/>With Pagination Callbacks
<DataTable data={users} columns={columns} pageIndex={page} pageSize={10} pageCount={totalPages} onPaginationChange={(pagination) => { setPage(pagination.pageIndex); }}/>Badge
Small label for status or categories.
import { Badge } from '@kit/ui/badge';<Badge>Default</Badge><Badge variant="secondary">Secondary</Badge><Badge variant="outline">Outline</Badge><Badge variant="destructive">Destructive</Badge>Avatar
User avatar with image and fallback.
import { Avatar, AvatarImage, AvatarFallback } from '@kit/ui/avatar';<Avatar> <AvatarImage src="/avatar.jpg" alt="User" /> <AvatarFallback>JD</AvatarFallback></Avatar>Profile Avatar
Avatar component with automatic initials fallback.
import { ProfileAvatar } from '@kit/ui/profile-avatar';{/* With image */}<ProfileAvatar displayName="John Doe" pictureUrl="/avatar.jpg"/>{/* Initials fallback */}<ProfileAvatar displayName="John Doe"/>{/* Single letter */}<ProfileAvatar text="J" />Empty State
Placeholder for empty content areas.
import { EmptyState, EmptyStateHeading, EmptyStateText, EmptyStateButton } from '@kit/ui/empty-state';<EmptyState> <EmptyStateHeading>No items found</EmptyStateHeading> <EmptyStateText> Get started by creating your first item. </EmptyStateText> <EmptyStateButton>Create Item</EmptyStateButton></EmptyState>With Icon
import { EmptyMedia } from '@kit/ui/empty-state';import { Inbox } from 'lucide-react';<EmptyState> <EmptyMedia variant="icon"> <Inbox /> </EmptyMedia> <EmptyStateHeading>No messages</EmptyStateHeading> <EmptyStateText>Your inbox is empty.</EmptyStateText></EmptyState>Skeleton
Loading placeholder animation.
import { Skeleton } from '@kit/ui/skeleton';{/* Text skeleton */}<Skeleton className="h-4 w-48" />{/* Avatar skeleton */}<Skeleton className="h-12 w-12 rounded-full" />{/* Card skeleton */}<div className="space-y-2"> <Skeleton className="h-4 w-full" /> <Skeleton className="h-4 w-3/4" /> <Skeleton className="h-4 w-1/2" /></div>Chart
Chart components using Recharts.
import { ChartContainer, ChartTooltip, ChartTooltipContent } from '@kit/ui/chart';import { Bar, BarChart, XAxis, YAxis } from 'recharts';const chartConfig = { value: { label: 'Value', color: 'hsl(var(--primary))', },};<ChartContainer config={chartConfig}> <BarChart data={data}> <XAxis dataKey="name" /> <YAxis /> <ChartTooltip content={<ChartTooltipContent />} /> <Bar dataKey="value" fill="var(--color-value)" /> </BarChart></ChartContainer>In MakerKit, we use DataTable for all admin list views (members, invoices, projects). The consistent pagination and sorting patterns reduce cognitive load for users navigating between sections.
Common Pitfalls
- DataTable missing column types: Always define
ColumnDef<T>[]with proper typing. Missing types cause runtime errors and poor autocompletion. - Skeleton size mismatch: Match skeleton dimensions to actual content. A text skeleton should match the expected text height, not be arbitrarily sized.
- Avatar without fallback: Always provide
AvatarFallbackfor when images fail to load. Use initials or a default icon. - Empty state not centered: EmptyState needs a container with proper height. Use
min-h-[400px]or similar to give it vertical space. - Chart config color mismatch: Chart colors use CSS variables. Ensure
--color-{key}matches your chartConfig keys exactly. - DataTable pagination state: Pagination is controlled externally. Manage
pageIndexin your component state and update viaonPaginationChange.
Frequently Asked Questions
When should I use Table vs DataTable?
How do I add sorting to DataTable columns?
Can I use ProfileAvatar without an image?
How do I make skeletons animate?
What chart types does ChartContainer support?
Next: Feedback & Loading →