ComponentsFeedback

Spinner

A small turning mark for a short wait inside one control: a button that is saving, a step Flo is filling. Whole pages and regions use a skeleton instead.

Playground

Change the controls to try each prop. The code under the preview updates as you go and lists only the props you changed; copy it into your page.

import { Spinner } from '@/ui/spinner';<Spinner label="Loading" />

Default

A 16px spinner that turns once a second. Screen readers hear "Loading".

import { Spinner } from '@/ui/spinner';

export default function SpinnerDefault() {
  return <Spinner />;
}

Sizes

Two sizes, matching the icon sizes: 16px by default, and sm at 12px next to 12px text.

import { Spinner } from '@/ui/spinner';

export default function SpinnerSizes() {
  return (
    <div className="flex items-center gap-300">
      <Spinner />
      <Spinner size="sm" />
    </div>
  );
}

Colour

The spinner takes the colour of the text around it: ink, subtle, or white on the primary button.

import { Button } from '@/ui/button';
import { Spinner } from '@/ui/spinner';

export default function SpinnerColour() {
  return (
    <div className="flex items-center gap-300">
      <span className="text-text">
        <Spinner />
      </span>
      <span className="text-text-subtle">
        <Spinner />
      </span>
      <Button variant="primary" loading>
        Save changes
      </Button>
    </div>
  );
}

In a button

Press the button to see it. Pass loading to the button rather than placing a spinner yourself: the spinner covers the label, the width holds and a second press is ignored.

import { useState } from 'react';
import { Button } from '@/ui/button';

export default function SpinnerInAButton() {
  const [saving, setSaving] = useState(false);
  const save = () => {
    setSaving(true);
    setTimeout(() => setSaving(false), 2000);
  };
  return (
    <Button variant="primary" loading={saving} loadingLabel="Saving" onClick={save}>
      Save changes
    </Button>
  );
}

Inline status

A 12px spinner with a word beside it, for a step that is still running.

Flo is filling
import { Spinner } from '@/ui/spinner';

export default function SpinnerInlineStatus() {
  return (
    <span className="inline-flex items-center gap-050 text-body-small leading-body-small font-medium text-text-subtle">
      <Spinner size="sm" label="Flo is filling the CV" />
      Flo is filling
    </span>
  );
}

On this page