Empty state for Stripe Apps
Show clear, helpful messaging when no data is available so users understand what to do next.
Empty states appear when there’s no data to display. They show on first use, after filtering returns zero results or when all items have been removed. An empty state explains why the view is empty and offers a clear next step.
Before you begin
Before you implement empty state patterns, make sure that you:
- Create an app or use an existing one.
- Install @stripe/ui-extension-sdk version 9. 2. 0 or later.
- Review the DataTable and Box component references.
Best practices
Keep these principles in mind when you build empty states:
- Explain why it’s empty : Tell users why there’s no data or content to display. “No transactions” is less helpful than “No transactions yet.”
- Guide users towards the next action : Prompt users to create the first record. The title and action text work as a call-and-response, for example “No customers yet” paired with “Add customer.” If the entire app requires setup first, use the onboarding pattern instead.
Write empty state content
- Title : State what’s missing. Use a short phrase with a full stop. Don’t promote or explain in the title.
- Good: “No successful payments.”
- Bad: “Try creating your first payment to get started!”
- Description : Explain when or how data appears. Keep sentences under 14 words. Use active voice.
- Good: “Payments appear here after your first sale.”
- Bad: “Once a payment has been successfully processed by the system, it will be displayed in this section.”
- Action text : Match the title. If the title says “No customers,” the action says “Add customer,” not “Get started.”
Use built-in empty messages for tables
The DataTable component accepts an emptyMessage prop that renders a styled empty state with an optional call to action. Use this instead of building a custom empty state inside the table.
Pass an object with message and action to show a call to action:
Pass a plain string when no action is needed:
<DataTable
columns={columns}
items={items}
emptyMessage="No results match your filters."
/>
Swap the empty message based on context
A table can be empty for different reasons. Swap the emptyMessage value dynamically to show the right message for each scenario. Use an object with a call to action when no data exists yet, and a plain string when filters produce zero results.
This keeps the empty state relevant to the user’s current context without requiring custom rendering logic outside the table. Pair the filter-specific message with a Clear filters link in the filter bar so users can reset without scrolling.
Common mistake
Don’t show a “create first item” call to action when items exist but are filtered out. The data might already exist. The empty state must reflect why it isn’t visible right now.
The emptyMessage prop only renders when items is an empty array. It doesn’t render while the table is in a loading state. Use the loading prop to handle that phase separately.
Build a section-level empty state
When only one section of a multi-section page is empty, use a compact message that doesn’t dominate the layout. Include a short title and a description that explains when data appears.
<Box
css={{
stack: 'y',
gap: 'xsmall',
alignX: 'center',
padding: 'large',
borderStyle: 'dashed',
borderColor: 'neutral',
borderRadius: 'medium',
borderWidth: 1,
}}
>
<Inline css={{font: 'body'}}>No recent activity.</Inline>
<Inline css={{font: 'caption', color: 'secondary', textAlign: 'center'}}>
Activity appears here after your first transaction.
</Inline>
</Box>
Use a dashed border with borderWidth: 1 to distinguish the empty area from surrounding content. Keep the title in font: 'body' (no bold) and the description in font: 'caption' with color: 'secondary' and textAlign: 'center'. This mirrors the empty state pattern used across the Stripe Dashboard.
Differentiate empty state from error state
An empty state means no data exists or matches the current filters. An error state means something went wrong while fetching. Keep these visually and semantically distinct:
| Scenario | What to show |
|---|---|
| Data loaded successfully, zero results | Empty state. Explain what belongs here. |
| Network request failed | Error state. Explain the failure and offer retry. |
| Data is still loading | Loading indicator. Show the Spinner. |
Render your view state in this order: loading first, then error if the fetch failed, then empty if the result set is empty, then content.
if (isLoading) {
return <LoadingState />;
}
if (error) {
return <ErrorState onRetry={refetch} />;
}
if (items.length === 0) {
return <EmptyState />;
}
return <ItemsList items={items} />;
