Skip to content
BigBang

Popular

    ↑↓Navigate↵OpenEscClose
    TR

    Patterns

    Empty states

    What to say and which action to offer on first use, when nothing matches and when everything is done.

    An empty area should tell users what is going on and what comes next. This pattern separates three empty states: first use, no results and done.

    • First use: when the user has not created anything yet. One primary action starts the work.
    • No results: when a search or filter matches no record. It says what was searched and offers to clear the filters.
    • Done: when all items are handled. No action is needed.

    The three kinds side by side; each is an EmptyState. It has an icon, a heading that names the state, a one-sentence explanation and at most one action.

    Live example, StorybookOpen in Storybook

    No projects yet

    Projects group work and share it with your team.

    No results for “Report”

    Check the spelling or loosen the filters.

    All items are handled

    No pending items are left.

    import { Button, Card, createDataTableColumnHelper, DataTable, EmptyState, Grid, type IconName } from '@bigbang/ui';
    import type { ReactNode } from 'react';
    import { type Lang, LangProvider } from '../lang';
    const copy = {
    tr: {
    firstUse: { heading: 'Henüz proje yok', text: 'Projeler işi gruplar ve ekibinizle paylaşır.', action: 'Proje oluştur' },
    noResults: { heading: '“Rapor” için sonuç yok', text: 'Yazımı denetleyin ya da filtreleri gevşetin.', action: 'Filtreleri temizle' },
    cleared: { heading: 'Tüm öğeler işlendi', text: 'Bekleyen öğe kalmadı.' },
    tableCaption: 'Proje listesi',
    tableName: 'Ad',
    tableEmpty: 'Henüz proje yok',
    },
    en: {
    firstUse: { heading: 'No projects yet', text: 'Projects group work and share it with your team.', action: 'Create project' },
    noResults: { heading: 'No results for “Report”', text: 'Check the spelling or loosen the filters.', action: 'Clear filters' },
    cleared: { heading: 'All items are handled', text: 'No pending items are left.' },
    tableCaption: 'Project list',
    tableName: 'Name',
    tableEmpty: 'No projects yet',
    },
    };
    export type EmptyKind = 'first-use' | 'no-results' | 'cleared' | 'table';
    /** One empty state: a decorative icon, a heading that names the state, one sentence and at most one primary action. */
    function Empty({ icon, heading, text, action }: { icon: IconName; heading: string; text: string; action?: string }) {
    return (
    <Card variant="outlined" padding="md">
    <EmptyState size="sm" headingLevel={3} icon={icon} title={heading} description={text} actions={action ? <Button>{action}</Button> : undefined} />
    </Card>
    );
    }
    type Row = { id: string; name: string };
    const helper = createDataTableColumnHelper<Row>();
    /** The three kinds of empty state, built with EmptyState (in a Card here) and Button. Pass `kind` to show one. */
    export function EmptyStatesDemo({ lang = 'tr', kind }: { lang?: Lang; kind?: EmptyKind }) {
    const t = copy[lang];
    const columns = [helper.accessor('name', { header: t.tableName, meta: { rowHeader: true } })];
    const parts: Record<EmptyKind, ReactNode> = {
    'first-use': <Empty icon="plus" heading={t.firstUse.heading} text={t.firstUse.text} action={t.firstUse.action} />,
    'no-results': <Empty icon="search" heading={t.noResults.heading} text={t.noResults.text} action={t.noResults.action} />,
    cleared: <Empty icon="circle-check" heading={t.cleared.heading} text={t.cleared.text} />,
    table: <DataTable<Row> columns={columns} data={[]} getRowId={(row) => row.id} caption={t.tableCaption} captionHidden status="empty" messages={{ empty: t.tableEmpty }} />,
    };
    return (
    <LangProvider lang={lang}>
    {kind ? (
    <div className="max-w-md">{parts[kind]}</div>
    ) : (
    <Grid columns={{ base: 1, md: 3 }} gap="lg">
    {parts['first-use']}
    {parts['no-results']}
    {parts.cleared}
    </Grid>
    )}
    </LangProvider>
    );
    }

    An empty table never shows the header row alone. When DataTable has the status empty, it shows one message row.

    Live example, StorybookOpen in Storybook
    Project list

    No projects yet

    import { Button, Card, createDataTableColumnHelper, DataTable, EmptyState, Grid, type IconName } from '@bigbang/ui';
    import type { ReactNode } from 'react';
    import { type Lang, LangProvider } from '../lang';
    const copy = {
    tr: {
    firstUse: { heading: 'Henüz proje yok', text: 'Projeler işi gruplar ve ekibinizle paylaşır.', action: 'Proje oluştur' },
    noResults: { heading: '“Rapor” için sonuç yok', text: 'Yazımı denetleyin ya da filtreleri gevşetin.', action: 'Filtreleri temizle' },
    cleared: { heading: 'Tüm öğeler işlendi', text: 'Bekleyen öğe kalmadı.' },
    tableCaption: 'Proje listesi',
    tableName: 'Ad',
    tableEmpty: 'Henüz proje yok',
    },
    en: {
    firstUse: { heading: 'No projects yet', text: 'Projects group work and share it with your team.', action: 'Create project' },
    noResults: { heading: 'No results for “Report”', text: 'Check the spelling or loosen the filters.', action: 'Clear filters' },
    cleared: { heading: 'All items are handled', text: 'No pending items are left.' },
    tableCaption: 'Project list',
    tableName: 'Name',
    tableEmpty: 'No projects yet',
    },
    };
    export type EmptyKind = 'first-use' | 'no-results' | 'cleared' | 'table';
    /** One empty state: a decorative icon, a heading that names the state, one sentence and at most one primary action. */
    function Empty({ icon, heading, text, action }: { icon: IconName; heading: string; text: string; action?: string }) {
    return (
    <Card variant="outlined" padding="md">
    <EmptyState size="sm" headingLevel={3} icon={icon} title={heading} description={text} actions={action ? <Button>{action}</Button> : undefined} />
    </Card>
    );
    }
    type Row = { id: string; name: string };
    const helper = createDataTableColumnHelper<Row>();
    /** The three kinds of empty state, built with EmptyState (in a Card here) and Button. Pass `kind` to show one. */
    export function EmptyStatesDemo({ lang = 'tr', kind }: { lang?: Lang; kind?: EmptyKind }) {
    const t = copy[lang];
    const columns = [helper.accessor('name', { header: t.tableName, meta: { rowHeader: true } })];
    const parts: Record<EmptyKind, ReactNode> = {
    'first-use': <Empty icon="plus" heading={t.firstUse.heading} text={t.firstUse.text} action={t.firstUse.action} />,
    'no-results': <Empty icon="search" heading={t.noResults.heading} text={t.noResults.text} action={t.noResults.action} />,
    cleared: <Empty icon="circle-check" heading={t.cleared.heading} text={t.cleared.text} />,
    table: <DataTable<Row> columns={columns} data={[]} getRowId={(row) => row.id} caption={t.tableCaption} captionHidden status="empty" messages={{ empty: t.tableEmpty }} />,
    };
    return (
    <LangProvider lang={lang}>
    {kind ? (
    <div className="max-w-md">{parts[kind]}</div>
    ) : (
    <Grid columns={{ base: 1, md: 3 }} gap="lg">
    {parts['first-use']}
    {parts['no-results']}
    {parts.cleared}
    </Grid>
    )}
    </LangProvider>
    );
    }
    Rule Value
    Primary action 1 at most. “Create project” on first use; “Clear filters” for no results; none when done.
    Heading Names the state: “No projects yet”. Do not write “No data”.
    Explanation One sentence, 25 words at most.
    No results Shows the searched text in the heading: “No results for “Report””.
    Empty table The header grid is never shown alone; use status="empty" or "filtered-empty".
    Illustration Optional and decorative; it carries no meaning and is aria-hidden.
    Heading level Follows the page outline; headingLevel={3} in the example; the small size size="sm" fits inside a card.

    No projects yet

    DoThe heading names the state and one action opens the way.

    No data

    Empty.
    Don'tA generic heading and two competing actions give no direction.
    1. When data has loaded and there are no records, the kind of state is decided: nothing created, a filter matched nothing, or everything is handled.
    2. The matching empty state takes the place of the list area. The page title and filter bar stay where they are.
    3. The primary action starts the work or clears the filters. After clearing, the list refills and focus moves to the first control.
    4. After first use, when the first record is created, the empty state turns into the list.
    • The heading is a real heading element; screen reader users find the state with heading navigation.
    • The icon and any illustration carry aria-hidden; meaning is given in text.
    • The action is a real button with a visible name.
    • When the state follows a search or filter, the change is announced politely as in the Filtering pattern.
    • Colour never carries meaning alone (WCAG 1.4.1).
    • The heading names the state: “No projects yet”, “All items are handled”.
    • The explanation gives the next step or the reason: “Projects group work and share it with your team.”
    • For no results, quote the searched text and give a way out.
    • Button text starts with a verb and has no full stop: “Create project”.
    • Do not blame the user; do not write “You found nothing”.
    Item Motion Preset
    Empty state appearing 110 ms fade; no shift motion.reveal
    Primary button 0.97 scale on press motion.press
    After refresh and filter No enter motion none

    With reduced motion, scales and shifts are removed and only the short fade stays.

    EmptyState, Card, Button, DataTable. Related patterns: Filtering, Bulk actions.