Mischief

55 / Feedback

Empty Row

One line saying a list came back empty, for a table, a list, or a popover.

Fund1YExpense
No funds match these filters.

Installation

Copy the source into your project, or keep it behind a package.

npx shadcn@latest add Tinkerers-Labs/mischief-ui/empty-row
import { EmptyRow } from "mischief-ui/empty-row"

Or paste it in yourself. The source imports the shared cn helper from @/lib/utils, so point that at your own copy.

registry/default/empty-row/empty-row.tsx
import * as React from "react" import { cn } from "@/lib/utils" export type EmptyRowProps = Omit<  React.HTMLAttributes<HTMLElement>,  "children"> & {  children?: React.ReactNode  /**   * Renders a table row spanning this many columns. Without it the row is a   * paragraph, for a list or a popover.   */  colSpan?: number

Usage

export function Results({ rows }) {
  if (rows.length === 0) {
    return <EmptyRow>No funds match these filters.</EmptyRow>
  }

  return <FundsTable rows={rows} />
}

The smallest of three

Empty Row is a line inside something. Empty State fills a panel with a title, a sentence, and a way forward. Not Found fills a page. They are separate components rather than sizes of one, because a filtered table wants a line and a blank page wants a heading, and a component that tries to be both is wrong at one end.

Reach for this one where the surrounding thing already explains itself: a table under its own heading, a search popover, a filter pane. There is nothing to introduce, only a result to report.

Inside a table

A paragraph is not valid inside a table body, and a row that does not span the columns leaves the message wedged under the first one. Pass colSpan and the component renders the row and the cell for you.

<tbody>
  {rows.length === 0 ? (
    <EmptyRow colSpan={columns.length}>Nothing filed yet.</EmptyRow>
  ) : (
    rows.map((row) => <Row key={row.id} {...row} />)
  )}
</tbody>

API

childrenReactNodeThe line itself. Defaults to "No matches."
colSpannumberRenders a table row spanning this many columns.
classNamestringClasses for the element.

Accessibility

A row inside a table is a real table row spanning every column, so the table's shape stays intact and a screen reader reads the message once rather than as a stray cell. Elsewhere it is a paragraph, announced by whatever region already holds the list. It is not a live region: put it in one only if the list can empty while someone is reading it.