A generic centered placeholder for empty lists, grids, or search results: an optional icon, a description, and an optional action. Modeled on AntD's `Empty` — reusable anywhere something has nothing to show yet.
registry/behivetech/molecules/empty-state at v0.3.4, snapshotted 2026-11-09
empty-state.tsx
1import { type HTMLAttributes, type ReactNode } from "react";2import { getClassName } from "@bht-component-registry/class-names";3import styles from "./empty-state.module.scss";45export interface EmptyStateProps extends HTMLAttributes<HTMLDivElement> {6 /** Additional class names to merge with the component root element */7 className?: string;8 /** Icon or illustration shown above the description */9 icon?: ReactNode;10 /** Message explaining the empty state */11 description: ReactNode;12 /** Action content rendered below the description (typically a Button) */13 children?: ReactNode;14}1516/**17 * Generic centered placeholder for empty lists/grids/results: icon, a18 * description, and an optional action. Modeled on AntD's `Empty` — reusable19 * anywhere something has nothing to show yet, not tied to any one feature.20 */21export const EmptyState = ({22 className,23 icon,24 description,25 children,26 ...rest27}: EmptyStateProps) => {28 const [rootClass, getChildClass] = getClassName({29 className,30 rootClass: "BHT__EmptyState",31 styles,32 });3334 return (35 <div {...rest} className={rootClass}>36 {icon !== undefined && (37 <div className={getChildClass("icon")}>{icon}</div>38 )}39 <p className={getChildClass("description")}>{description}</p>40 {children !== undefined && (41 <div className={getChildClass("action")}>{children}</div>42 )}43 </div>44 );45};
empty-state.module.scss
1.BHT__EmptyState {2 display: flex;3 flex-direction: column;4 align-items: center;5 justify-content: center;6 gap: var(--spacing-4);7 text-align: center;8 color: var(--md-sys-color-on-surface-variant, var(--color-text-muted));9 font-family: var(--md-sys-typescale-body-medium-font, var(--font-body));10 font-size: var(--md-sys-typescale-body-medium-size, 0.875rem);11 font-weight: var(--md-sys-typescale-body-medium-weight, 400);12 line-height: var(--md-sys-typescale-body-medium-line-height, 1.25rem);13 letter-spacing: var(--md-sys-typescale-body-medium-tracking, 0.0179em);1415 &__icon {16 font-size: 2.5rem;17 line-height: 1;18 opacity: 0.4;19 }2021 &__description {22 margin: 0;23 }2425 &__action {26 display: inline-flex;27 }28}
index.ts
1export { EmptyState } from "./empty-state.js";2export type { EmptyStateProps } from "./empty-state.js";
empty-state.example.tsx
1import { EmptyState } from "./empty-state.js";23export const BasicEmptyState = () => (4 <EmptyState description="No blocks yet." />5);67export const EmptyStateWithIcon = () => (8 <EmptyState icon="🧩" description="No blocks yet. Add one to get started." />9);1011export const EmptyStateWithAction = () => (12 <EmptyState icon="🧩" description="No blocks yet. Add one to get started.">13 <button onClick={() => {}}>+ Add Block</button>14 </EmptyState>15);