Summit
ComponentsActionsButton

Loading Button

A Loading Button shows users that a request is in progress by displaying a spinner in place of its label.

When to Use

  • for an action that waits on a request, such as “Save changes”
  • when a Button would otherwise be disabled while its request is in progress

Avoid disabling a Button while its request is in progress. If the Button has focus when it becomes disabled, focus moves to the page. LoadingButton keeps focus and reports aria-busy.

DoUse a Loading Button while the request is in progress so that focus stays on the Button.
Don’tWhen the Button is disabled while it’s working, keyboard users lose focus to the page, and focus doesn’t return when the Button is enabled again.

Anatomy

ElementUsage
ContentRequired
ContainerRequired
SpinnerLoading state only

The content is the label and the icons of a Button. While the Button is loading, a Spinner is displayed over the content.

Behavior

The spinner appears 150ms after loading becomes true, so users never see it if the request finishes sooner. When the spinner has appeared, it stays for at least 400ms.

While the Button is loading, and until the spinner has disappeared, it has aria-busy and aria-disabled, and it keeps focus. Clicks, Enter, and Space are ignored. The label fades out and keeps its position, so the width of the Button doesn’t change.

Accessibility

A Loading Button has the role and the keys of a Button. Keyboard users can still move focus to it with Tab while it’s loading. The spinner is hidden from assistive technology, so the accessible name is still the label.

Installation

npx shadcn@latest add @summit/loading-button

The CLI also adds @summit/button and @summit/spinner.

Usage

To show the spinner while a request is in progress, pass its pending state to loading.

import { LoadingButton } from '@/components/ui/loading-button';

<LoadingButton loading={saving}>Save changes</LoadingButton>;

API Reference

PropTypeDefaultDescription
loadingbooleanRequiredShows the spinner after 150ms and keeps it visible for at least 400ms

Other props are passed to the Button that LoadingButton renders. See Button for the variants and the sizes.

On this page