Docs
Empty states

Empty states

Convey the absence of data and provide clear instruction for what to do about it.

At a minimum, empty states convey the fact that there is nothing to list, perform, or display on the current page. They should also provide a clear call to action for the user to take.

Missing route

Users may accidentally navigate to a non-existent dynamic route, such as a non-existent bucket in Storage or a non-existent table in the Table Editor. In these cases, follow the pattern of a centered Admonition as shown below..

Loading...
import { Admonition } from 'ui-patterns/admonition'
import Link from 'next/link'
import { Button } from 'ui'
 
const bucketId = 'user_avatars'
 
export function EmptyStateMissingRoute() {
  return (
    <div className="flex items-center justify-center w-full h-full">
      <Admonition
        type="default"
        className="max-w-md"
        title="Unable to find bucket"
        description={`${bucketId ? `The bucket “${bucketId}` : 'This bucket'} doesn’t seem to exist.`}
      >
        <Button asChild type="default" className="mt-2">
          <Link href="/">Head back</Link>
        </Button>
      </Admonition>
    </div>
  )
}

Zero results

Tabular information without results—or perhaps no data to begin with—should have an empty state that matches the larger presentation.

For instance, a Table may just display a single row just like it would if it had data. Dulling the TableHead text color and removing the TableCell hover state can further reinforce the lack of usable data.

Loading...
import { Card, Table, TableBody, TableHead, TableHeader, TableRow, TableCell } from 'ui'
 
export function EmptyStateZeroItemsTable() {
  return (
    <Card className="w-full">
      <Table>
        <TableHeader>
          <TableRow>
            <TableHead className="text-foreground-muted">Table name</TableHead>
            <TableHead className="text-foreground-muted">Date created</TableHead>
            <TableHead />
          </TableRow>
        </TableHeader>
        <TableBody>
          <TableRow className="[&>td]:hover:bg-inherit">
            <TableCell colSpan={3}>
              <p className="text-sm text-foreground">No tables yet</p>
              <p className="text-sm text-foreground-lighter">Connect a table from your database</p>
            </TableCell>
          </TableRow>
        </TableBody>
      </Table>
    </Card>
  )
}

The treatment for other layouts, such as the list of users in Authentication, should match their own general styling.

Initial state

Perhaps the user has not yet created any data yet. They might be a feature for the first time. In these cases, the empty state should provide the briefest information about the lack of data, putting more focus on the value proposition and primary action.

Loading...
import { Button } from 'ui'
import { Plus } from 'lucide-react'
import { BucketAdd } from 'icons'
 
export function EmptyStateInitialState() {
  return (
    <aside className="border border-dashed w-full bg-surface-100 rounded-lg px-4 py-10 flex flex-col gap-y-4 items-center text-center gap-1 text-balance">
      <div className="flex flex-col gap-3 items-center text-center">
        <BucketAdd size={24} strokeWidth={1.5} className="text-foreground-muted" />
        <div className="flex flex-col gap-1">
          <h3>Create a vector bucket</h3>
          <p className="text-foreground-light text-sm">
            Store, index, and query your vector embeddings at scale.
          </p>
        </div>
      </div>
      <Button size="tiny" type="primary" className="w-fit" icon={<Plus size={14} />}>
        Create bucket
      </Button>
    </aside>
  )
}

Keep in mind that this empty state will likely appear after a visual loading state. Consider layout shift and button placement during and after the transition.

Components

There is not yet a shared empty state UI component. The context and needs for each placement differ enough to warrant custom components for each placement. That said, we should aim to make these as consistent as possible over time. See the below examples that might share common logic in a future centralized component.

External references