Button
Trigger actions and events with accessible, styled buttons. Supports variants, colors, icons, loading states, active state, avatar, and can render as links.
Playground
Experiment with different props in real-time.
Basic Usage
Import and use the Button component with a label prop.
<script lang="ts">
import { Button } from 'sv5ui';
</script>
<Button label="Button" />Variants
6 visual styles for different levels of emphasis.
<Button variant="solid" label="Solid" />
<Button variant="outline" label="Outline" />
<Button variant="soft" label="Soft" />
<Button variant="subtle" label="Subtle" />
<Button variant="ghost" label="Ghost" />
<Button variant="link" label="Link" />Colors
8 semantic color schemes.
<Button color="primary" label="Primary" />
<Button color="secondary" label="Secondary" />
<Button color="tertiary" label="Tertiary" />
<Button color="success" label="Success" />
<Button color="warning" label="Warning" />
<Button color="error" label="Error" />
<Button color="info" label="Info" />
<Button color="surface" label="Surface" />Sizes
5 sizes from extra small to extra large.
<Button size="xs" label="Extra Small" />
<Button size="sm" label="Small" />
<Button size="md" label="Medium" />
<Button size="lg" label="Large" />
<Button size="xl" label="Extra Large" />Icon Only
Use the icon prop for icon-only buttons. Add square for equal width and height.
<!-- Icon only -->
<Button icon="lucide:plus" />
<!-- Icon only with square aspect ratio -->
<Button icon="lucide:settings" square />
<Button icon="lucide:heart" variant="outline" square />
<Button icon="lucide:trash-2" variant="soft" color="error" square />Leading & Trailing Icons
Add icons before or after the label with leadingIcon and trailingIcon. Use trailing to move a single icon to the trailing side.
<!-- Leading icon -->
<Button leadingIcon="lucide:download" label="Download" />
<!-- Trailing icon -->
<Button trailingIcon="lucide:arrow-right" label="Next" />
<!-- Both icons -->
<Button leadingIcon="lucide:mail" trailingIcon="lucide:send" label="Send" />
<!-- Trailing prop: moves single icon to trailing side -->
<Button icon="lucide:arrow-right" label="Continue" trailing />Loading
Show a spinner and disable interaction with the loading prop. Customize the spinner icon with loadingIcon.
<!-- Default loading -->
<Button label="Loading" loading />
<!-- Custom loading icon -->
<Button label="Refreshing" loading loadingIcon="lucide:refresh-cw" />
<!-- Loading with variant -->
<Button label="Saving" loading variant="outline" />
<Button label="Loading" loading variant="soft" />Loading Auto
Use loadingAuto to automatically show a loading spinner while the onclick handler's promise is pending.
<script lang="ts">
import { Button } from 'sv5ui';
function handleSave() {
return new Promise((resolve) => setTimeout(resolve, 2000));
}
</script>
<!-- Automatically shows loading while the promise is pending -->
<Button label="Save Changes" loadingAuto onclick={handleSave} />
<Button label="Submit" loadingAuto onclick={handleSave} variant="outline" />Disabled
Disable the button to prevent interaction.
<Button label="Disabled" disabled />
<Button label="Disabled" disabled variant="outline" />
<Button label="Disabled" disabled variant="soft" />Block (Full Width)
Stretch the button to full width with the block prop.
<Button label="Full Width Button" block />
<Button label="Full Width Outline" variant="outline" block />
<Button label="Full Width Soft" variant="soft" block />Button Type
Set the HTML type attribute. Useful in forms for submit and reset behavior.
<form onsubmit|preventDefault={() => alert('Submitted!')}>
<Button type="submit" label="Submit" color="primary" />
<Button type="reset" label="Reset" variant="outline" color="surface" />
<Button type="button" label="Cancel" variant="ghost" />
</form>As Link
Pass an href prop to render as an anchor element. Use external to automatically add target="_blank" and rel="noopener noreferrer".
<!-- Internal link -->
<Button href="/docs" label="Go to Docs" />
<!-- External link with external prop -->
<Button href="https://github.com" external label="GitHub" leadingIcon="mdi:github" />
<!-- External adds target="_blank" and rel="noopener noreferrer" automatically -->Active State
Control active state manually with active, or let it auto-detect from the current URL when using href. Use exact for exact pathname matching. Customize the active appearance with activeColor, activeVariant, activeClass, and inactiveClass.
<!-- Manually set active state -->
<Button label="Active" active variant="ghost" activeVariant="soft" />
<!-- Active with different color -->
<Button label="Active" active activeColor="success" variant="ghost" />
<!-- Auto-detected from URL (with exact matching) -->
<Button href="/docs" label="Docs" variant="ghost" activeVariant="soft" activeColor="primary" exact />
<!-- Custom active/inactive classes -->
<Button
href="/docs"
label="Custom Active"
variant="ghost"
activeClass="font-bold ring-2 ring-primary"
inactiveClass="opacity-60"
/>Avatar
Display an avatar before the label with the avatar prop. It accepts AvatarProps.
<Button
avatar={{ src: 'https://i.pravatar.cc/32?u=1', alt: 'John' }}
label="John Doe"
/>
<Button
avatar={{ src: 'https://i.pravatar.cc/32?u=2', alt: 'Jane' }}
label="Jane Smith"
variant="outline"
/>
<Button
avatar={{ src: 'https://i.pravatar.cc/32?u=3', alt: 'User' }}
label="Profile"
trailingIcon="lucide:chevron-down"
variant="ghost"
/>Element Reference
Bind to the root DOM element with bind:ref for imperative access.
<script lang="ts">
import { Button } from 'sv5ui';
let buttonRef = $state<HTMLElement | null>(null);
function inspectButton() {
if (!buttonRef) return;
console.log('Tag:', buttonRef.tagName);
console.log('Size:', buttonRef.offsetWidth, 'x', buttonRef.offsetHeight);
console.log('Text:', buttonRef.textContent?.trim());
console.log('Classes:', buttonRef.className);
}
</script>
<Button bind:ref={buttonRef} label="Inspect Me" leadingIcon="lucide:search" onclick={inspectButton} />UI Slots
Use the ui prop to override classes on specific internal elements of the Button.
| Slot | Description |
|---|---|
base | Root button element — controls shape, padding, shadow, etc. |
label | Text label wrapper — controls font, spacing, text styles |
leadingIcon | Icon before the label — controls icon size, color, animation |
trailingIcon | Icon after the label — controls icon size, color, animation |
leadingAvatar | Avatar before the label — controls avatar wrapper styles |
leadingAvatarSize | Avatar size wrapper — controls avatar dimensions |
<Button
label="Custom Styled"
leadingIcon="lucide:star"
ui={{
base: 'rounded-full shadow-lg',
label: 'font-bold tracking-wide',
leadingIcon: 'text-yellow-400'
}}
/><Button
label="Gradient Button"
leadingIcon="lucide:sparkles"
ui={{
base: 'bg-gradient-to-r from-purple-500 to-pink-500 border-none text-white hover:from-purple-600 hover:to-pink-600',
leadingIcon: 'animate-pulse'
}}
/>Snippets
Use snippets to fully replace the leading or trailing content with custom markup.
| Snippet | Replaces | Description |
|---|---|---|
children | label | Custom label content — use for rich text, inline elements, etc. |
leadingSlot | leadingIcon / avatar | Custom content before the label — replaces icon and avatar |
trailingSlot | trailingIcon | Custom content after the label — replaces trailing icon |
<Button variant="outline" color="primary">
{#snippet leadingSlot()}
<div class="flex h-5 w-5 items-center justify-center rounded-full bg-primary">
<span class="text-[10px] font-bold text-on-primary">AI</span>
</div>
{/snippet}
Ask Claude
{#snippet trailingSlot()}
<Kbd value="⌘K" size="sm" />
{/snippet}
</Button><Button variant="soft" color="success">
{#snippet leadingSlot()}
<span class="relative flex h-2.5 w-2.5">
<span class="absolute inline-flex h-full w-full animate-ping rounded-full bg-success opacity-75"></span>
<span class="relative inline-flex h-2.5 w-2.5 rounded-full bg-success"></span>
</span>
{/snippet}
Online
</Button>Props
| Prop | Type | Default |
|---|---|---|
variant | 'solid' | 'outline' | 'soft' | 'subtle' | 'ghost' | 'link' | 'solid' |
color | 'primary' | 'secondary' | 'tertiary' | 'success' | 'warning' | 'error' | 'info' | 'surface' | 'primary' |
size | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' |
label | string | - |
icon | string | - |
leadingIcon | string | - |
trailingIcon | string | - |
trailing | boolean | false |
loading | boolean | false |
loadingAuto | boolean | false |
loadingIcon | string | 'lucide:loader-circle' |
disabled | boolean | false |
block | boolean | false |
square | boolean | false |
href | string | - |
type | 'button' | 'submit' | 'reset' | 'button' |
external | boolean | false |
active | boolean | - |
exact | boolean | false |
activeColor | ColorType | - |
activeVariant | VariantType | - |
activeClass | string | - |
inactiveClass | string | - |
avatar | AvatarProps | - |
ref | HTMLElement | null | null |
class | string | - |
ui | Record<Slot, Class> | - |