Mantine Spinner

Logo

@gfazioli/mantine-spinner

A Mantine React component offers customizable loading animations to enhance the user experience in React applications.

Installation

yarn add @gfazioli/mantine-spinner

After installation import package styles at the root of your application:

import '@gfazioli/mantine-spinner/styles.css';

You can import styles within a layer @layer mantine-spinner by importing @gfazioli/mantine-spinner/styles.layer.css file.

import '@gfazioli/mantine-spinner/styles.layer.css';

Usage

Note: The Spinner component renders only on the client side to avoid hydration mismatches in SSR (Server-Side Rendering) environments. This is necessary because the SVG coordinates are calculated with floating-point math, which can differ between server and client. If you use SSR, the spinner will appear only after the client has mounted.

The Spinner enables the creation of a captivating spinner effect, which enhances the visual dynamics of a user interface. This effect can be used to draw attention, indicate loading processes, or provide an interactive experience that engages users effectively. By implementing this spinner effect, developers can improve the overall aesthetic appeal and functionality of their applications, ensuring a more seamless and enjoyable user experience.

Size
Inner
Segments
Thickness
Duration
Stroke linecap
Glow
Color
Gradient from
Gradient to
import { Spinner } from "@gfazioli/mantine-spinner";

function Demo() {
  return <Spinner />;
}

Segment shapes

Use the segmentShape prop to change the shape of individual segments. Available shapes are line (default), dot (filled circles), and arc (curved path segments along the circumference).

Line

Dot

Arc

import { Spinner } from "@gfazioli/mantine-spinner";
import { Group, Stack, Text } from "@mantine/core";

function Demo() {
  return (
    <Group align="center" justify="center" gap={60}>
      <Stack align="center" gap="xs">
        <Spinner size={80} segments={16} thickness={4} inner={24} segmentShape="line" />
        <Text size="sm" c="dimmed">Line</Text>
      </Stack>
      <Stack align="center" gap="xs">
        <Spinner size={80} segments={16} thickness={4} inner={24} segmentShape="dot" />
        <Text size="sm" c="dimmed">Dot</Text>
      </Stack>
      <Stack align="center" gap="xs">
        <Spinner size={80} segments={16} thickness={4} inner={24} segmentShape="arc" />
        <Text size="sm" c="dimmed">Arc</Text>
      </Stack>
    </Group>
  );
}

Trail variant

The trail variant creates a classic sweeping tail effect, similar to the iOS/macOS activity indicator. Each segment fades out linearly, producing a smooth trailing animation.

import { Spinner } from "@gfazioli/mantine-spinner";
import { Group } from "@mantine/core";

function Demo() {
  return (
    <Group align="center" justify="center" gap={40}>
      <Spinner size={80} segments={12} thickness={4} inner={24} variant="trail" color="blue" />
      <Spinner size={80} segments={16} thickness={3} inner={28} variant="trail" color="teal" />
      <Spinner
        size={80}
        segments={20}
        thickness={3}
        inner={32}
        variant="trail"
        color="violet"
        direction="counter-clockwise"
      />
    </Group>
  );
}

Neon effect

Combine variant="trail" with glow and a bright color for a neon tube effect. Set minOpacity to a value like 0.3 so segments appear always slightly lit. Control the glow intensity by passing a number to glow.

import { Spinner } from "@gfazioli/mantine-spinner";
import { Group } from "@mantine/core";

function Demo() {
  return (
    <Group align="center" justify="center" gap={40}>
      <Spinner
        size={80}
        segments={16}
        thickness={4}
        inner={24}
        variant="trail"
        glow={3}
        color="cyan"
        minOpacity={0.3}
      />
      <Spinner
        size={80}
        segments={16}
        thickness={4}
        inner={24}
        variant="trail"
        glow={5}
        color="lime"
        minOpacity={0.3}
      />
      <Spinner
        size={80}
        segments={16}
        thickness={4}
        inner={24}
        variant="trail"
        glow={3}
        gradient={{ from: "pink", to: "violet" }}
        minOpacity={0.3}
      />
    </Group>
  );
}

Glow

The glow prop adds a bloom/glow effect to any variant using an SVG gaussian blur filter. It can be combined with any variant, gradient, or color.

import { Spinner } from "@gfazioli/mantine-spinner";
import { Group } from "@mantine/core";

function Demo() {
  return (
    <Group align="center" justify="center" gap={40}>
      <Spinner size={80} segments={16} thickness={4} inner={24} glow={2} color="blue" />
      <Spinner
        size={80}
        segments={16}
        thickness={4}
        inner={24}
        glow={4}
        variant="trail"
        color="teal"
      />
      <Spinner
        size={80}
        segments={24}
        thickness={4}
        inner={24}
        glow={6}
        gradient={{ from: "orange", to: "red" }}
      />
    </Group>
  );
}

Hue rotate

The hueRotate prop continuously cycles through the color spectrum, creating a rainbow effect on the entire spinner. It can be combined with any variant, glow, and gradient for spectacular results.

import { Spinner } from "@gfazioli/mantine-spinner";
import { Group } from "@mantine/core";

function Demo() {
  return (
    <Group align="center" justify="center" gap={40}>
      <Spinner size={80} segments={16} thickness={4} inner={24} hueRotate color="blue" />
      <Spinner
        size={80}
        segments={24}
        thickness={4}
        inner={24}
        hueRotate
        glow={4}
        gradient={{ from: "blue", to: "cyan" }}
      />
      <Spinner
        size={80}
        segments={16}
        thickness={4}
        inner={24}
        hueRotate
        glow={3}
        variant="trail"
        color="cyan"
        minOpacity={0.3}
      />
    </Group>
  );
}

Gradient

Use the gradient prop to interpolate colors across segments. The gradient transitions smoothly from from to to, overriding both color and colors.

import { Spinner } from "@gfazioli/mantine-spinner";
import { Group } from "@mantine/core";

function Demo() {
  return (
    <Group align="center" justify="center" gap={40}>
      <Spinner
        size={80}
        segments={24}
        thickness={4}
        inner={24}
        gradient={{ from: "blue", to: "cyan" }}
      />
      <Spinner
        size={80}
        segments={24}
        thickness={4}
        inner={24}
        gradient={{ from: "pink", to: "violet" }}
        variant="trail"
      />
      <Spinner
        size={80}
        segments={24}
        thickness={4}
        inner={24}
        gradient={{ from: "orange", to: "yellow" }}
      />
    </Group>
  );
}

Progress

Set the progress prop (0–100) to use the spinner as a determinate progress indicator. Animation is disabled and segments fill proportionally. Combine with children to display a label.

import { useState } from "react";
import { Spinner } from "@gfazioli/mantine-spinner";
import { Center, Slider, Stack, Text } from "@mantine/core";

function Demo() {
  const [progress, setProgress] = useState(65);

  return (
    <Stack align="center" gap="lg">
      <Center h={200}>
        <Spinner
          size={120}
          segments={24}
          thickness={6}
          inner={38}
          progress={progress}
          color="teal"
        >
          <Text fw={700} size="lg">{progress}%</Text>
        </Spinner>
      </Center>
      <Slider
        w={300}
        min={0}
        max={100}
        value={progress}
        onChange={setProgress}
        label={`${progress}%`}
      />
    </Stack>
  );
}

Spinner.Group

Spinner.Group stacks multiple spinners concentrically, aligning them on the same center point. This is useful for creating layered ring effects.

import { Spinner } from "@gfazioli/mantine-spinner";
import { Center } from "@mantine/core";

function Demo() {
  return (
    <Center h={200}>
      <Spinner.Group>
        <Spinner size={120} segments={20} thickness={4} inner={44} color="blue" duration={1400} />
        <Spinner
          size={70}
          segments={14}
          thickness={3}
          inner={22}
          color="cyan"
          direction="counter-clockwise"
          duration={1000}
        />
        <Spinner size={30} segments={8} thickness={2} inner={8} color="violet" duration={800} />
      </Spinner.Group>
    </Center>
  );
}

Spinner.Overlay

Spinner.Overlay wraps content and displays a semi-transparent overlay with a centered spinner on top. Control visibility with the visible prop and blur with the blur prop.

Card content

This content is covered by a loading overlay when the spinner is visible. Toggle the button below to show or hide it.

import { useState } from "react";
import { Spinner } from "@gfazioli/mantine-spinner";
import { Button, Card, Group, Stack, Text } from "@mantine/core";

function Demo() {
  const [loading, setLoading] = useState(true);

  return (
    <Stack align="center" gap="lg">
      <Spinner.Overlay
        visible={loading}
        blur={2}
        spinnerProps={{ size: 50, segments: 16, thickness: 4, inner: 16 }}
      >
        <Card shadow="sm" padding="lg" radius="md" withBorder w={360}>
          <Text fw={500} size="lg" mb="xs">Card content</Text>
          <Text size="sm" c="dimmed">
            This content is covered by a loading overlay when the spinner is visible.
            Toggle the button below to show or hide it.
          </Text>
        </Card>
      </Spinner.Overlay>
      <Group>
        <Button onClick={() => setLoading((v) => !v)}>
          {loading ? "Hide" : "Show"} overlay
        </Button>
      </Group>
    </Stack>
  );
}

Styled

Of course, you can style the spinner as you like. For example, by adding an additional animation effect on the whole spinner.

import { Spinner } from "@gfazioli/mantine-spinner";
import classes from './Spinner.module.css';

function Demo() {
  return (
    <Group align="center" justify="center" gap={100}>
      <Spinner className={classes.pulse} />
      <Spinner className={classes.rotate} />
    </Group>
  );
}

Example: Random

Random spinner effect. Use it to create a loading effect.

<Spinner
  size={undefined}
  inner={undefined}
  segments={undefined}
  thickness={undefined}
  duration={undefined}
  variant="undefined"
  direction="undefined"
  strokeLinecap="undefined"
/>