# Loading for Stripe Apps Show loading indicators so people know your app is working and the interface stays responsive. Loading states communicate that your app is retrieving data. A well-implemented loading pattern builds trust by keeping the interface responsive and preventing layout shifts when content appears. ## Before you begin Before you implement loading patterns, make sure your app meets these requirements: - [Create an app](https://docs.stripe.com/stripe-apps/create-app.md) or use an existing one. - Install `@stripe/ui-extension-sdk` version `9.x` or later. - Review the [Spinner](https://docs.stripe.com/stripe-apps/components/spinner.md) component reference. ## Best practices - **Immediate feedback**: Show a loading indicator as soon as a data fetch begins. - **Appropriate scope**: Match the loading indicator to the scope of what’s loading. Use the full-page spinner for entire views, and inline spinners for sections. - **Prevent flash**: Use the `delay` prop on the Spinner to avoid showing indicators for fast operations. > Some components handle loading states internally through built-in props. For example, Button uses a `pending` prop instead of requiring a manual Spinner. Before you build a custom loading pattern, review the component reference to see if built-in loading behavior is available. ## Show a full-page loading state Show a centered Spinner with `size="large"` that fills the available space when your entire view depends on a single data fetch. ```jsx if (isLoading) { return ( ); } ``` ## Show a section-level loading state Use `size="medium"` when other parts of the page are already visible. If needed, load sections independently so available content appears immediately while other sections finish loading. ## Use the delay prop to prevent flash Use `` to prevent the spinner from appearing if data arrives within the delay window. **Recommended delay values:** - **200–300ms** for most data fetches - **0ms** for known-slow operations - **100ms** for view transitions ## Handle loading in tabbed layouts Place the loading state inside each tab component, not around the `Tabs` container. Keep the tab bar visible and interactive. > Don’t wrap `Tabs` in a loading state. If you show a spinner instead of the tab bar, users lose their navigation context and might think the app is broken instead of loading. ## Handle loading for actions When a user triggers an action like saving or submitting, use the [Button](https://docs.stripe.com/stripe-apps/components/button.md) component’s built-in `pending` prop. This sets the button to a pending visual style and disables it, which helps prevent double submission without requiring you to manually compose a Spinner. ```jsx import {useState} from 'react'; import {Button} from '@stripe/ui-extension-sdk/ui'; const SaveButton = ({onSave}) => { const [isSaving, setIsSaving] = useState(false); const handleSave = async () => { setIsSaving(true); try { await onSave(); } finally { setIsSaving(false); } }; return ( ); }; ``` The `pending` prop handles the loading indicator and disabled state together—you don’t need to add a Spinner inside the button manually. ## Choose the right spinner size | Size | Use case | | --- | --- | | `large` | Full-page or full-tab loading. Someone is waiting for primary content. | | `medium` | Section-level loading. Other parts visible. | | `small` | Inline indicators within table cells or compact UI elements. | ## See also - [Spinner component for Stripe Apps](https://docs.stripe.com/stripe-apps/components/spinner.md) - [Button component for Stripe Apps](https://docs.stripe.com/stripe-apps/components/button.md) - [Full-page apps](https://docs.stripe.com/stripe-apps/patterns/full-page-apps.md)