Unified UI

Button

A versatile button component for Laravel Blade with multiple variants, sizes, and icon support.

Overview

The Button component provides a consistent, accessible button element for your Laravel applications. It supports multiple variants, sizes, icons, and loading states — all rendered server-side with Blade.

Installation

composer require unified-ui/laravel

Copy the Blade component file into your project:

cp vendor/unified-ui/laravel/resources/views/components/button.blade.php \
   resources/views/components/ui/button.blade.php

Usage

Basic

<x-ui-button>Click me</x-ui-button>

Variants

<x-ui-button variant="primary">Primary</x-ui-button>
<x-ui-button variant="secondary">Secondary</x-ui-button>
<x-ui-button variant="outline">Outline</x-ui-button>
<x-ui-button variant="ghost">Ghost</x-ui-button>
<x-ui-button variant="destructive">Destructive</x-ui-button>
<x-ui-button variant="link">Link</x-ui-button>

Sizes

<x-ui-button size="sm">Small</x-ui-button>
<x-ui-button size="md">Medium</x-ui-button>
<x-ui-button size="lg">Large</x-ui-button>
<x-ui-button size="icon">
    <x-lucide-plus class="size-4" />
</x-ui-button>

With Icons

<x-ui-button icon="lucide-mail">
    Send Email
</x-ui-button>

<x-ui-button icon-right="lucide-arrow-right">
    Next Step
</x-ui-button>

Loading State

<x-ui-button :loading="$isSubmitting">
    Submit
</x-ui-button>

When loading is true, the button displays a spinner and becomes disabled automatically.

<x-ui-button href="/dashboard" variant="outline">
    Go to Dashboard
</x-ui-button>

When href is provided, the component renders an <a> tag instead of a <button>.

Disabled

<x-ui-button :disabled="true">Disabled</x-ui-button>

Full Width

<x-ui-button full>Full Width Button</x-ui-button>

Blade Component

{{-- resources/views/components/ui/button.blade.php --}}
@props([
    'variant' => 'primary',
    'size' => 'md',
    'href' => null,
    'icon' => null,
    'iconRight' => null,
    'loading' => false,
    'disabled' => false,
    'full' => false,
    'type' => 'button',
])

@php
    $baseClasses = 'inline-flex items-center justify-center font-medium rounded-md transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50';

    $variants = [
        'primary'     => 'bg-primary text-primary-foreground hover:bg-primary/90',
        'secondary'   => 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
        'outline'     => 'border border-border bg-background hover:bg-accent hover:text-accent-foreground',
        'ghost'       => 'hover:bg-accent hover:text-accent-foreground',
        'destructive' => 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
        'link'        => 'text-primary underline-offset-4 hover:underline',
    ];

    $sizes = [
        'sm'   => 'h-8 px-3 text-xs gap-1.5',
        'md'   => 'h-9 px-4 text-sm gap-2',
        'lg'   => 'h-10 px-6 text-base gap-2',
        'icon' => 'h-9 w-9',
    ];

    $classes = implode(' ', [
        $baseClasses,
        $variants[$variant] ?? $variants['primary'],
        $sizes[$size] ?? $sizes['md'],
        $full ? 'w-full' : '',
    ]);

    $tag = $href ? 'a' : 'button';
@endphp

<{{ $tag }}
    @if($href) href="{{ $href }}" @endif
    @if($tag === 'button') type="{{ $type }}" @endif
    @disabled($disabled || $loading)
    {{ $attributes->merge(['class' => $classes]) }}
>
    @if($loading)
        <x-ui-spinner size="sm" class="animate-spin" />
    @elseif($icon)
        <x-dynamic-component :component="$icon" class="size-4" />
    @endif

    {{ $slot }}

    @if($iconRight && !$loading)
        <x-dynamic-component :component="$iconRight" class="size-4" />
    @endif
</{{ $tag }}>

Props

PropTypeDefaultDescription
variantstringprimaryVisual style: primary, secondary, outline, ghost, destructive, link
sizestringmdSize: sm, md, lg, icon
hrefstring|nullnullIf provided, renders as an <a> tag
iconstring|nullnullLeading icon Blade component name
iconRightstring|nullnullTrailing icon Blade component name
loadingboolfalseShows spinner and disables the button
disabledboolfalseDisables the button
fullboolfalseMakes the button full width
typestringbuttonHTML button type (button, submit, reset)

All additional attributes (e.g. wire:click, x-on:click, id, data-*) are forwarded to the underlying element via Blade's $attributes bag.

Livewire Example

<x-ui-button wire:click="save" :loading="$wire.isProcessing">
    Save Changes
</x-ui-button>

Alpine.js Example

<div x-data="{ loading: false }">
    <x-ui-button
        x-on:click="loading = true; $dispatch('submit')"
        x-bind:disabled="loading"
    >
        <span x-show="!loading">Submit</span>
        <span x-show="loading">Processing…</span>
    </x-ui-button>
</div>

On this page