Spinner

An indicator that can be used to show a loading state.

Loading...
<div class="flex w-full max-w-xs flex-col gap-4 [--radius:1rem]">
    <twig:Item variant="muted">
        <twig:Item:Media>
            <twig:Spinner />
        </twig:Item:Media>
        <twig:Item:Content>
            <twig:Item:Title class="line-clamp-1">Processing payment...</twig:Item:Title>
        </twig:Item:Content>
        <twig:Item:Content class="flex-none justify-end">
            <span class="text-sm tabular-nums">$100.00</span>
        </twig:Item:Content>
    </twig:Item>
</div>

Installation

bin/console ux:install spinner --kit shadcn

That's it!

Install the following Composer dependencies:

composer require symfony/ux-icons tales-from-a-dev/twig-tailwind-extra:^1.0.0

Copy the following file(s) into your Symfony app:

templates/components/Spinner.html.twig
{%- props label = 'Loading' -%}
<twig:ux:icon
    name="lucide:loader-2"
    data-slot="spinner"
    role="status"
    aria-label="{{ label }}"
    class="{{ ('size-4 animate-spin ' ~ attributes.render('class'))|tailwind_merge }}"
    {{ ...attributes.without('class') }}
/>

Happy coding!

Usage

<twig:Button disabled>
    <twig:Spinner /> Please wait
</twig:Button>

Examples

Size

Use the size-* utility class to change the size of the spinner.

Loading...
<div class="flex items-center gap-6">
    <twig:Spinner class="size-3" />
    <twig:Spinner class="size-4" />
    <twig:Spinner class="size-6" />
    <twig:Spinner class="size-8" />
</div>

Button

Add a spinner to a button to indicate a loading state. Place the Spinner before the label with data-icon="inline-start" for a start position, or after the label with data-icon="inline-end" for an end position.

Loading...
<div class="flex flex-col items-center gap-4">
    <twig:Button disabled size="sm">
        <twig:Spinner data-icon="inline-start" />
        Loading...
    </twig:Button>
    <twig:Button variant="outline" disabled size="sm">
        <twig:Spinner data-icon="inline-start" />
        Please wait
    </twig:Button>
    <twig:Button variant="secondary" disabled size="sm">
        <twig:Spinner data-icon="inline-start" />
        Processing
    </twig:Button>
</div>

Badge

Add a spinner to a badge to indicate a loading state. Place the Spinner before the label with data-icon="inline-start" for a start position, or after the label with data-icon="inline-end" for an end position.

Loading...
<div class="flex items-center gap-4 [--radius:1.2rem]">
    <twig:Badge>
        <twig:Spinner data-icon="inline-start" />
        Syncing
    </twig:Badge>
    <twig:Badge variant="secondary">
        <twig:Spinner data-icon="inline-start" />
        Updating
    </twig:Badge>
    <twig:Badge variant="outline">
        <twig:Spinner data-icon="inline-start" />
        Processing
    </twig:Badge>
</div>

Input Group

Loading...
<div class="flex w-full max-w-md flex-col gap-4">
    <twig:InputGroup>
        <twig:InputGroup:Input placeholder="Send a message..." disabled />
        <twig:InputGroup:Addon align="inline-end">
            <twig:Spinner />
        </twig:InputGroup:Addon>
    </twig:InputGroup>
    <twig:InputGroup>
        <twig:InputGroup:Textarea placeholder="Send a message..." disabled />
        <twig:InputGroup:Addon align="block-end">
            <twig:Spinner /> Validating...
            <twig:InputGroup:Button class="ml-auto" variant="default">
                <twig:ux:icon name="lucide:arrow-up" />
                <span class="sr-only">Send</span>
            </twig:InputGroup:Button>
        </twig:InputGroup:Addon>
    </twig:InputGroup>
</div>

Empty

Loading...
<twig:Empty class="w-full">
    <twig:Empty:Header>
        <twig:Empty:Media variant="icon">
            <twig:Spinner />
        </twig:Empty:Media>
        <twig:Empty:Title>Processing your request</twig:Empty:Title>
        <twig:Empty:Description>Please wait while we process your request. Do not refresh the page.</twig:Empty:Description>
    </twig:Empty:Header>
    <twig:Empty:Content>
        <twig:Button variant="outline" size="sm">Cancel</twig:Button>
    </twig:Empty:Content>
</twig:Empty>

RTL

To enable RTL support, set the dir="rtl" attribute on the root element.

Loading...
<div class="flex flex-col gap-8">
    {# Arabic #}
    <div class="flex w-full max-w-xs flex-col gap-4 [--radius:1rem]" dir="rtl">
        <twig:Item variant="muted" dir="rtl">
            <twig:Item:Media>
                <twig:Spinner />
            </twig:Item:Media>
            <twig:Item:Content>
                <twig:Item:Title class="line-clamp-1">جاري معالجة الدفع...</twig:Item:Title>
            </twig:Item:Content>
            <twig:Item:Content class="flex-none justify-end">
                <span class="text-sm tabular-nums">١٠٠.٠٠ دولار</span>
            </twig:Item:Content>
        </twig:Item>
    </div>
    {# Hebrew #}
    <div class="flex w-full max-w-xs flex-col gap-4 [--radius:1rem]" dir="rtl">
        <twig:Item variant="muted" dir="rtl">
            <twig:Item:Media>
                <twig:Spinner />
            </twig:Item:Media>
            <twig:Item:Content>
                <twig:Item:Title class="line-clamp-1">מעבד תשלום...</twig:Item:Title>
            </twig:Item:Content>
            <twig:Item:Content class="flex-none justify-end">
                <span class="text-sm tabular-nums">$100.00</span>
            </twig:Item:Content>
        </twig:Item>
    </div>
</div>