# 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](https://docs.stripe.com/stripe-apps/create-app.md) or use an existing one. - Install `@stripe/ui-extension-sdk` version `9.2.0` or later. - Review the [DataTable](https://docs.stripe.com/stripe-apps/components/datatable.md) and [Box](https://docs.stripe.com/stripe-apps/components/box.md) 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 toward 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](https://docs.stripe.com/stripe-apps/patterns/onboarding-experience.md) instead. ### Write empty state content - **Title**: State what’s missing. Use a short phrase with a period. 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](https://docs.stripe.com/stripe-apps/components/datatable.md) 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: ```jsx ``` Pass a plain string when no action is needed: ```jsx ``` ### 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. ```jsx ``` 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](https://docs.stripe.com/stripe-apps/patterns/filter-controls.md) so users can reset without scrolling. > 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. ```jsx No recent activity. Activity appears here after your first transaction. ``` 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](https://docs.stripe.com/stripe-apps/patterns/loading.md). 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. ```jsx if (isLoading) { return ; } if (error) { return ; } if (items.length === 0) { return ; } return ; ``` ## See also - [DataTable component for Stripe Apps](https://docs.stripe.com/stripe-apps/components/datatable.md) - [Filter controls pattern](https://docs.stripe.com/stripe-apps/patterns/filter-controls.md) - [Loading pattern for Stripe Apps](https://docs.stripe.com/stripe-apps/patterns/loading.md)