Components / Feedback

Spinner

Loading indicators for async operations.

Sizes

sm · 16px
md · 24px
lg · 40px

Colors

Primary
Grey
Success
Source SpinnerDemo.tsx

The exact code behind the live demo above. Fetch it raw at /components/source/spinner.txt.

import React from "react";

function Section({ title, children }: { title: string; children: React.ReactNode }) {
  return (
    <div className="mb-8">
      <h3 className="text-sm font-bold text-[var(--flux-heading)] mb-3">{title}</h3>
      <div className="p-6 rounded border border-[var(--flux-grey-100)] bg-[var(--flux-surface)]">
        {children}
      </div>
    </div>
  );
}

function Spinner({
  size,
  trackColor = "var(--flux-grey-200)",
  arcColor = "var(--flux-primary-400)",
}: {
  size: number;
  trackColor?: string;
  arcColor?: string;
}) {
  const strokeWidth = size < 24 ? 3 : 4;
  const radius = (size - strokeWidth) / 2;
  const circumference = 2 * Math.PI * radius;

  return (
    <svg
      className="animate-spin"
      width={size}
      height={size}
      viewBox={`0 0 ${size} ${size}`}
      fill="none"
    >
      <circle
        cx={size / 2}
        cy={size / 2}
        r={radius}
        stroke={trackColor}
        strokeWidth={strokeWidth}
      />
      <circle
        cx={size / 2}
        cy={size / 2}
        r={radius}
        stroke={arcColor}
        strokeWidth={strokeWidth}
        strokeDasharray={circumference}
        strokeDashoffset={circumference * 0.75}
        strokeLinecap="round"
      />
    </svg>
  );
}

export default function SpinnerDemo() {
  return (
    <div>
      <Section title="Sizes">
        <div className="flex items-center gap-8">
          <div className="flex flex-col items-center gap-2">
            <Spinner size={16} />
            <span className="text-xs text-[var(--flux-grey-300)]">sm · 16px</span>
          </div>
          <div className="flex flex-col items-center gap-2">
            <Spinner size={24} />
            <span className="text-xs text-[var(--flux-grey-300)]">md · 24px</span>
          </div>
          <div className="flex flex-col items-center gap-2">
            <Spinner size={40} />
            <span className="text-xs text-[var(--flux-grey-300)]">lg · 40px</span>
          </div>
        </div>
      </Section>

      <Section title="Colors">
        <div className="flex items-center gap-8">
          <div className="flex flex-col items-center gap-2">
            <Spinner size={24} arcColor="var(--flux-primary-400)" />
            <span className="text-xs text-[var(--flux-grey-300)]">Primary</span>
          </div>
          <div className="flex flex-col items-center gap-2">
            <Spinner size={24} arcColor="var(--flux-grey-300)" />
            <span className="text-xs text-[var(--flux-grey-300)]">Grey</span>
          </div>
          <div className="flex flex-col items-center gap-2">
            <Spinner size={24} arcColor="var(--flux-success)" />
            <span className="text-xs text-[var(--flux-grey-300)]">Success</span>
          </div>
        </div>
      </Section>
    </div>
  );
}