Page state

Pro

What a page shows instead of its content: nothing here yet (empty), nothing matches the filters (no-match), something broke (error), or it can't be shown to you (unavailable). The words are the caller's, since they are the product's. An error carries the request id to quote; "unavailable" is for both "doesn't exist" and "not yours", so it never reveals which.

Markdown

Preview

Install

With the Candor CLI

First time? Get started sets up the registry and theme. Or with shadcn directly: npx shadcn@latest add @candor/page-state

Brings in: button, copy-field, locale, skeleton, utils · npm: lucide-react@^1.67.0

Usage

TSX · The example above
"use client"

import { PageSkeleton, PageState } from "@/components/page-state"

export default function Example() {
  return (
    <div className="grid max-w-3xl gap-4 sm:grid-cols-2">
      <div className="flex rounded-lg border">
        <PageState
          kind="no-match"
          title="No migrations match these filters"
          detail="Nothing on Staging was rolled back in the last 30 days."
          secondary={{ label: "Clear filters", onClick: () => {} }}
        />
      </div>
      <div className="flex rounded-lg border">
        <PageState
          kind="error"
          title="The flags couldn’t load"
          detail="Keel couldn’t reach the flag service. Try again, or quote this id to support."
          requestId="req_7f3a91c2"
          action={{ label: "Try again", onClick: () => {} }}
        />
      </div>
      <div className="flex h-72 overflow-hidden rounded-lg border sm:col-span-2">
        <PageSkeleton label="Loading repositories" />
      </div>
    </div>
  )
}

<PageState>

PropTypeDefaultDescription
kindrequiredPageStateKind—empty, no-match, error or unavailable; sets the icon, and an error is announced.
titlerequiredstring—What's going on, in the product's words.
detailReact.ReactNode | undefined—More on why, or what to do, under the title.
action{ label: string; onClick: () => void } | undefined—The one thing to do next.
secondary{ label: string; onClick: () => void } | undefined—A quieter second option, e.g. clear filters.
requestIdstring | undefined—For errors: the id support needs.
IconLucideIcon | undefined—Replaces the kind's icon.
classNamestring | undefined—Extra classes for the outer element.

<PageSkeleton>

A page that's loading: the shape of what's coming, announced once.

PropTypeDefaultDescription
variant"list" | "detail" | undefined"list"The shape to sketch: rows of a list (the default) or one item's detail.
labelstring | undefined—What's loading, announced to screen readers. Defaults to "Loading".
classNamestring | undefined—Extra classes for the outer element.

Strings

The fixed words, in English by default. Change any of them under pageState in <LocaleProvider strings={{ … }}>; the table is exported as pageStateStrings.

KeyEnglish
requestIdRequest id
loadingLoading

Types

PageStateKind
"empty" | "no-match" | "error" | "unavailable"