No Data
The NoData component renders a centered empty state used across the application when a list, table, or view has no content (or after an error). It pairs an illustration with a title, one or more description lines, and optional primary / secondary / split-button actions.
Variations
Without actions
A minimal empty state — just an illustration, title, and description.
Installation
import { NoData } from "@harnessio/ui/components";
<NoData imageName="no-data-cog" title="No pipelines yet" description={["Create your first pipeline to get started."]} primaryButton={{ label: "Create pipeline", icon: "plus", to: "/pipelines/new", }} secondaryButton={{ label: "Learn more", variant: "outline" }}/>;Anatomy
NoData is a single, configuration-driven component — you describe the empty state with props rather than composing child parts.
<NoData imageName="no-data-cog" title="No pipelines yet" description={["Create your first pipeline to get started."]} primaryButton={{ label: "Create pipeline", icon: "plus" }}/>- Illustration — rendered only when
imageNameis set, above the title, atimageSizepixels (default112). - Title — a single centered line of emphasized text.
- Description — one line per entry in the
descriptionarray, each rendered as its own centered line of supporting text. - Actions row — rendered when any of
primaryButton,secondaryButton,splitButton, orchildrenis provided. They appear in that fixed order: primary, then secondary, then split button, then any customchildren. The actions are not mutually exclusive — any combination renders together. withBorder— wraps the whole block in a bordered, rounded container; when false (the default) the empty state is borderless.
The primaryButton extends Button props, so it accepts variant and other button options. The secondaryButton is always rendered as an outline button regardless of any variant passed. Passing to on either turns it into a router link; isDialogTrigger wraps it as a dialog trigger.
Best practices
Do
- Match the illustration to the situation — use a “no data” illustration for genuinely empty views and a “no results” illustration after a search or filter returns nothing.
- Give people a way forward: a primary action to create the missing thing, or a secondary action to learn more or clear filters.
- Keep the title short and the description to one or two lines.
Don’t
- Don’t use
NoDatafor loading states — use a skeleton or spinner while data is in flight, and only show the empty state once you know there’s nothing to display. - Don’t stack more than a primary and a secondary action; if you need a menu of choices, use the split button.
Content
- Write the title as a plain statement of what’s missing (“No pipelines yet”), not an error (“Failed to load”).
- Use the description to say what to do next, not to repeat the title.
- Each string in
descriptionis its own line — split sentences deliberately rather than relying on wrapping.
Accessibility
Labeling
- The
titleis rendered as styled text, not a semantic heading. If the empty state needs to participate in the page’s heading outline, provide a heading in the surrounding layout. - The illustration is decorative and is not given descriptive alt text — make sure the title and description carry the full meaning on their own.
Focus & states
NoDataitself is non-interactive; focus and keyboard behavior come entirely from the action buttons (or customchildren) you pass in, which are standardButtons.
Known limitations
- Split-button option handling is constrained. The
splitButtonrenders aSplitButton, but itshandleOptionChangecallback is only forwarded for a specific internal option value — prefer the primary/secondary actions for general use, and verify split-button behavior in context.
Properties
NoData
Prop | Required | Default | Type |
|---|---|---|---|
| title | true | string | |
| description | true | string[] | |
| imageName | false | IllustrationsNameType | |
| imageSize | false | 112 | number |
| primaryButton | false | ButtonProps & { label; icon?; to?; isDialogTrigger? } | |
| secondaryButton | false | ButtonProps & { label; icon?; to?; isDialogTrigger? } | |
| splitButton | false | { label; icon?; options; handleOptionChange; handleButtonClick } | |
| withBorder | false | false | boolean |
| children | false | ReactNode | |
| className | false | string | |
| textWrapperClassName | false | string |