Light

Empty

Displays a placeholder with an icon, title, and description for empty or zero-state content.

Spec · from metadata

When to use

  • No data to display (empty tables, lists, search results)
  • First-time user experience with no content created yet
  • Error states where content failed to load
  • Filtered results with no matches

When not to use

  • Loading states — use Skeleton or Spinner instead
  • Error messages in forms — use FieldError instead
  • Inline helper text — use descriptive text directly

Variants

PropValuesDefaultDescription
variant (EmptyMedia)defaulticondefaultdefault is transparent; icon adds a bg-muted rounded-lg container.

Anti-patterns

Avoid<div className="text-center p-8">
  <p>No results found</p>
</div>
Prefer<Empty>
  <EmptyHeader>
    <EmptyTitle>No results found</EmptyTitle>
    <EmptyDescription>Try adjusting your search or filters.</EmptyDescription>
  </EmptyHeader>
</Empty>

Use the Empty component for consistent empty state styling with proper structure.

Accessibility

  • Screen readerEmpty state should be informative. Include actionable guidance in the description. Links in description support underline styling.
  • ContrastTitle uses default foreground. Description uses text-muted-foreground.

Token bindings

TokenCategoryUsage
mutedcolorEmptyMedia icon variant background
foregroundcolorEmptyMedia icon variant text and EmptyTitle
muted-foregroundcolorEmptyDescription text
rounded-lgradiusContainer border radius

Import

import {
  Empty,
  EmptyContent,
  EmptyDescription,
  EmptyHeader,
  EmptyMedia,
  EmptyTitle,
} from "@timelycare/helix-ui"

Props

interface EmptyProps extends React.HTMLAttributes<HTMLDivElement> {
  className?: string
  children: React.ReactNode
}

interface EmptyHeaderProps extends React.HTMLAttributes<HTMLDivElement> {
  className?: string
  children: React.ReactNode
}

interface EmptyMediaProps extends React.HTMLAttributes<HTMLDivElement> {
  variant?: "default" | "icon"
  className?: string
  children: React.ReactNode
}

interface EmptyTitleProps extends React.HTMLAttributes<HTMLHeadingElement> {
  className?: string
  children: React.ReactNode
}

interface EmptyDescriptionProps extends React.HTMLAttributes<HTMLParagraphElement> {
  className?: string
  children: React.ReactNode
}

interface EmptyContentProps extends React.HTMLAttributes<HTMLDivElement> {
  className?: string
  children: React.ReactNode
}

Styling

PropertyValueTailwind
LayoutFlex column, centeredflex flex-col items-center justify-center
Text alignCentertext-center
Gap (sections)16pxgap-4
Padding24pxp-6

Typography

ElementFontSizeColor
TitleAdelle Sans Semibold16px (text-base)text-foreground
DescriptionAdelle Sans Regular14px (text-sm)text-muted-foreground

Media

VariantSizeStyle
icon48pxsize-12 text-muted-foreground — icon centered in a muted circular background
defaultFlexibleRenders children as-is (avatars, images, etc.)

Variants

Empty is a single-variant component. Visual variation comes from composition of sub-components and media variants.


Common Patterns

Basic Empty State

<Empty>
  <EmptyHeader>
    <EmptyMedia variant="icon">
      <InboxIcon />
    </EmptyMedia>
    <EmptyTitle>No messages</EmptyTitle>
    <EmptyDescription>You don't have any messages yet.</EmptyDescription>
  </EmptyHeader>
  <EmptyContent>
    <Button>Send a message</Button>
  </EmptyContent>
</Empty>

With Actions

<Empty>
  <EmptyHeader>
    <EmptyMedia variant="icon">
      <FolderIcon />
    </EmptyMedia>
    <EmptyTitle>No Projects Yet</EmptyTitle>
    <EmptyDescription>
      You haven't created any projects yet. Get started by creating your first project.
    </EmptyDescription>
  </EmptyHeader>
  <EmptyContent>
    <div className="flex gap-2">
      <Button>Create Project</Button>
      <Button variant="outline">Import Project</Button>
    </div>
  </EmptyContent>
  <Button variant="link" className="text-muted-foreground" size="sm" asChild>
    <a href="/docs">Learn More <ArrowUpRightIcon /></a>
  </Button>
</Empty>

With Avatar

<Empty>
  <EmptyHeader>
    <EmptyMedia variant="default">
      <Avatar className="size-12">
        <AvatarImage src="/user.png" />
        <AvatarFallback>JD</AvatarFallback>
      </Avatar>
    </EmptyMedia>
    <EmptyTitle>User Offline</EmptyTitle>
    <EmptyDescription>
      This user is currently offline. You can leave a message.
    </EmptyDescription>
  </EmptyHeader>
  <EmptyContent>
    <Button size="sm">Leave Message</Button>
  </EmptyContent>
</Empty>

404 / Search Empty State

<Empty>
  <EmptyHeader>
    <EmptyTitle>404 - Not Found</EmptyTitle>
    <EmptyDescription>
      The page you're looking for doesn't exist. Try searching below.
    </EmptyDescription>
  </EmptyHeader>
  <EmptyContent>
    <InputGroup className="sm:w-3/4">
      <InputGroupInput placeholder="Try searching..." />
      <InputGroupAddon>
        <SearchIcon />
      </InputGroupAddon>
    </InputGroup>
  </EmptyContent>
</Empty>

Icon Selection Guide

ContextIcon
No dataInbox
No search resultsSearch
No files/documentsFileText
No notificationsBell
No usersUsers
No messagesMessageSquare

Accessibility

  • Use semantic heading elements via EmptyTitle (renders as heading)
  • EmptyDescription provides context for screen readers
  • Action buttons within EmptyContent must be keyboard accessible
  • For icon-only media, the icon is decorative — screen readers skip it in favor of the title

Gotchas

ProblemSolution
Empty state not centered verticallyEnsure parent container has min-h-[300px] or similar height constraint
Icon too small or largeUse variant="icon" on EmptyMedia for consistent size-12 icon container
Multiple action buttons misalignedWrap in a div with flex gap-2 inside EmptyContent
Description text too wideConstrain the parent Empty width or use max-w-md on description

See Also

  • Related Components: Skeleton (loading placeholder), Card (container for empty states)
  • Patterns: Component Match — When to use Empty vs Skeleton

Last updated: February 19, 2026