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.
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:
<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:
- Disable the button - Add
:disabled="isLoading" to prevent double clicks
- Show the spinner - Add
<Spinner v-if="isLoading" class="mr-2" /> before the text
- Keep the text visible - The button text remains visible during loading
Spinner Customization
The Spinner component accepts a class prop for styling:
<!-- Smaller spinner -->
<Spinner v-if="isLoading" class="mr-2 size-3" />
<!-- Different color -->
<Spinner v-if="isLoading" class="mr-2 text-white" />
Examples
<Button :disabled="isSubmitting" @click="handleSubmit">
<Spinner v-if="isSubmitting" class="mr-2" />
{{ isSubmitting ? 'Saving...' : 'Save Changes' }}
</Button>
<Button
variant="destructive"
:disabled="isDeleting"
@click="handleDelete"
>
<Spinner v-if="isDeleting" class="mr-2" />
Delete
</Button>
With Validation
<Button
:disabled="!isFormValid || isSubmitting"
@click="handleSubmit"
>
<Spinner v-if="isSubmitting" class="mr-2" />
Submit
</Button>