> ## Documentation Index
> Fetch the complete documentation index at: https://docs.deploystack.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Button with Loading States

> Guide for implementing loading states in buttons using the Spinner component.

Use the shadcn-vue Spinner component inside buttons to show loading states during async operations.

## Component Locations

```
services/frontend/src/components/ui/button/
├── Button.vue      # Standard shadcn-vue button
└── index.ts        # Button variants and exports

services/frontend/src/components/ui/spinner/
├── Spinner.vue     # Animated loading spinner
└── index.ts        # Spinner exports
```

## Usage

Import both Button and Spinner, then conditionally render the Spinner inside the button:

```vue theme={null}
<script setup lang="ts">
import { ref } from 'vue'
import { Button } from '@/components/ui/button'
import { Spinner } from '@/components/ui/spinner'
import { toast } from 'vue-sonner'

const isSubmitting = ref(false)

const handleSubmit = async () => {
  isSubmitting.value = true
  try {
    await saveData()
    toast.success('Saved successfully')
  } catch (error) {
    toast.error('Save failed', { description: error.message })
  } finally {
    isSubmitting.value = false
  }
}
</script>

<template>
  <Button
    :disabled="isSubmitting"
    @click="handleSubmit"
  >
    <Spinner v-if="isSubmitting" class="mr-2" />
    Save Changes
  </Button>
</template>
```

## Pattern

The loading button pattern consists of three parts:

1. **Disable the button** - Add `:disabled="isLoading"` to prevent double clicks
2. **Show the spinner** - Add `<Spinner v-if="isLoading" class="mr-2" />` before the text
3. **Keep the text visible** - The button text remains visible during loading

## Spinner Customization

The Spinner component accepts a `class` prop for styling:

```vue theme={null}
<!-- Smaller spinner -->
<Spinner v-if="isLoading" class="mr-2 size-3" />

<!-- Different color -->
<Spinner v-if="isLoading" class="mr-2 text-white" />
```

## Examples

### Form Submit Button

```vue theme={null}
<Button :disabled="isSubmitting" @click="handleSubmit">
  <Spinner v-if="isSubmitting" class="mr-2" />
  {{ isSubmitting ? 'Saving...' : 'Save Changes' }}
</Button>
```

### Delete Button

```vue theme={null}
<Button
  variant="destructive"
  :disabled="isDeleting"
  @click="handleDelete"
>
  <Spinner v-if="isDeleting" class="mr-2" />
  Delete
</Button>
```

### With Validation

```vue theme={null}
<Button
  :disabled="!isFormValid || isSubmitting"
  @click="handleSubmit"
>
  <Spinner v-if="isSubmitting" class="mr-2" />
  Submit
</Button>
```

## Related Documentation

* [UI Design System](/development/frontend/ui/) - Overall design patterns
* [Global Sonner Toast System](/development/frontend/ui/design-global-sonner) - Toast notifications
