Skip to main content

Loading components

Introduction

Modern, accessible loading components built with Heroicons and Tailwind CSS. These components are designed for Storefront and Dashboard contexts and follow WCAG 2.1 AA standards.

Available components

Modern spinner using a Heroicons circular path with smooth rotation animation.

{{-- Basic usage --}}
<x-loading.spinner />

{{-- With custom text --}}
<x-loading.spinner text="Loading publication..." />

{{-- Different sizes --}}
<x-loading.spinner size="sm" />
<x-loading.spinner size="md" /> {{-- default --}}
<x-loading.spinner size="lg" />
<x-loading.spinner size="xl" />

{{-- Inline with content --}}
<x-loading.spinner :inline="true" text="Processing..." />

{{-- Custom accessibility label --}}
<x-loading.spinner ariaLabel="Loading user dashboard" />

2. <x-loading.circular-spinner /> – Circular spinner with customizable stroke width

Same design as the main spinner but with configurable stroke width for different visual styles.

{{-- Basic usage --}}
<x-loading.circular-spinner />

{{-- With custom stroke width --}}
<x-loading.circular-spinner
size="lg"
text="Loading reader..."
:strokeWidth="6"
/>

3. <x-loading.pulse /> – Simple pulse animations

Lightweight loading indicators using pure Tailwind animations.

{{-- Circle variant (default) --}}
<x-loading.pulse />

{{-- Three pulsing dots --}}
<x-loading.pulse variant="dots" text="Loading..." />

{{-- Three pulsing bars --}}
<x-loading.pulse variant="bars" size="sm" :inline="true" />

4. <x-loading /> or <x-loading.index /> – Default loading component

Backwards-compatible wrapper that uses the modern spinner.

<x-loading />

Common props

All loading components share these props:

PropTypeDefaultDescription
sizestring'md'Size: 'sm', 'md', 'lg', 'xl'
textstringnullOptional loading text to display
inlinebooleanfalseDisplay inline with text vs centered block
ariaLabelstringautoCustom accessibility label

Accessibility

  • WCAG 2.1 AA compliant
  • role="status" for screen reader announcements
  • aria-label with contextual loading messages
  • aria-hidden="true" on decorative SVG elements
  • tw-sr-only fallback text when no visible text provided
  • High contrast colors using tw-violet-600

Internationalization (i18n)

  • By default, ariaLabel falls back to translations: when no text is provided it uses trans('misc.loading'); when text is present the default label becomes "Loading: {text}".
  • Provide explicit, contextual labels via ariaLabel using translations and ensure keys exist in all locales (lang/en/, lang/es/, lang/pt/, ...).
{{-- Default (uses trans('misc.loading')) --}}
<x-loading.spinner />

{{-- Contextual label using translations --}}
<x-loading.spinner ariaLabel="{{ trans('accessibility.loading_user_dashboard') }}" />

Example translation entries (add to each locale):

// lang/en/misc.php
return [
'loading' => 'Loading',
];

// lang/en/accessibility.php
return [
'loading_user_dashboard' => 'Loading user dashboard',
];

Usage guidelines

When to use each component

  • spinner: Default choice for most loading states
  • circular-spinner: When you need the exact original design
  • pulse: For minimal, subtle loading indicators
  • index: For backward compatibility with existing code

Best practices

{{-- ✅ Descriptive loading text --}}
<x-loading.spinner text="Saving changes..." />

{{-- ✅ Appropriate size for context --}}
<x-loading.spinner size="sm" :inline="true" text="Uploading..." />

{{-- ❌ Avoid generic text --}}
<x-loading.spinner text="Loading..." />

{{-- ❌ Avoid missing context for screen readers --}}
<div>Loading...</div>

Performance notes

  • spinner: Pure Tailwind tw-animate-spin, no custom CSS
  • circular-spinner: Pure Tailwind tw-animate-spin with custom stroke width
  • pulse: Pure Tailwind tw-animate-pulse, no custom CSS
  • All components: Zero custom CSS, 100% Tailwind animations

Migration from legacy loading

Replace old loading implementations:

{{-- Before --}}
<div class="Loading">Loading...</div>

{{-- After --}}
<x-loading.spinner text="Loading..." />

Examples in context

Button loading state

<button 
wire:click="saveData"
wire:loading.attr="disabled"
class="tw-btn tw-btn-primary"
>
<span wire:loading.remove>Save Changes</span>
<span wire:loading class="tw-flex tw-items-center tw-gap-2">
<x-loading.spinner size="sm" />
Saving...
</span>
</button>

Table loading state

<div wire:loading.class="tw-opacity-50" wire:target="search">
{{-- Table content --}}
</div>

<div wire:loading wire:target="search" class="tw-absolute tw-inset-0 tw-flex tw-items-center tw-justify-center tw-bg-white/80">
<x-loading.spinner text="Searching..." />
</div>
<x-modal.dialog wire:model="showModal">
<x-slot name="content">
@if($loading)
<x-loading.spinner text="Loading data..." />
@else
{{-- Modal content --}}
@endif
</x-slot>
</x-modal.dialog>
  • resources/views/components/loading/
  • resources/views/components/loading/spinner.blade.php
  • resources/views/components/loading/circular-spinner.blade.php
  • resources/views/components/loading/pulse.blade.php

Updated for Laravel 10, Livewire 2, Alpine 2, and Tailwind 3.

X

Graph View