RingProgress

Give user feedback for status of the task with circle diagram

PackageIcon

Usage

The sections prop accepts an array of objects with the following properties:

  • value – number between 0 and 100 representing the percentage of the ring to fill
  • color – segment color from theme.colors or any CSS color value
  • tooltip (optional) – React node to display when hovering over the section
  • Any valid SVG <circle> element props (onClick, onMouseEnter, style, etc.)

Note: Section values should sum to 100% or less for expected behavior. Values exceeding 100% total will cause sections to overlap.

Application data usage

import { RingProgress, Text } from '@mantine/core';

function Demo() {
  return (
    <RingProgress
      label={
        <Text size="xs" ta="center">
          Application data usage
        </Text>
      }
      sections={[
        { value: 40, color: 'cyan' },
        { value: 15, color: 'orange' },
        { value: 15, color: 'grape' },
      ]}
    />
  );
}

Size, thickness & rounded caps

Use the size, thickness & roundCaps props to configure the RingProgress size and thickness values:

Size
Thickness
import { RingProgress } from '@mantine/core';
function Demo() {
  return (
    <RingProgress
      size={120}
      thickness={12}
      roundCaps
      sections={[
        { value: 40, color: 'cyan' },
        { value: 15, color: 'orange' },
        { value: 15, color: 'grape' },
      ]}
    />
  )
}

Sections tooltips

Add a tooltip property to a section to display a floating Tooltip when the user hovers over it:

Hover sections to see tooltips

import { RingProgress, Text } from '@mantine/core';

function Demo() {
  return (
    <RingProgress
      size={170}
      thickness={16}
      label={
        <Text size="xs" ta="center" px="xs" style={{ pointerEvents: 'none' }}>
          Hover sections to see tooltips
        </Text>
      }
      sections={[
        { value: 40, color: 'cyan', tooltip: 'Documents – 40 Gb' },
        { value: 25, color: 'orange', tooltip: 'Apps – 25 Gb' },
        { value: 15, color: 'grape', tooltip: 'Other – 15 Gb' },
      ]}
    />
  );
}

Section gaps

Use the sectionGap prop to add visual separation between sections. The gap is specified in degrees:

No gap (default)

5° gap

10° gap

import { RingProgress, Stack, Text } from '@mantine/core';

function Demo() {
  const sections = [
    { value: 40, color: 'cyan' },
    { value: 25, color: 'orange' },
    { value: 15, color: 'grape' },
  ];

  return (
    <Stack align="center">
      <div>
        <Text size="sm" ta="center" mb="xs">
          No gap (default)
        </Text>
        <RingProgress sections={sections} />
      </div>

      <div>
        <Text size="sm" ta="center" mb="xs">
          5° gap
        </Text>
        <RingProgress sections={sections} sectionGap={5} />
      </div>

      <div>
        <Text size="sm" ta="center" mb="xs">
          10° gap
        </Text>
        <RingProgress sections={sections} sectionGap={10} />
      </div>
    </Stack>
  );
}

Start angle

Use the startAngle prop to control where the progress starts. The angle is specified in degrees, where 0 = right, 90 = bottom, 180 = left, and 270 = top (default):

0° (right)

90° (bottom)

180° (left)

270° (top)

import { Group, RingProgress, Stack, Text } from '@mantine/core';

function Demo() {
  const sections = [{ value: 40, color: 'cyan' }];

  return (
    <Group justify="center">
      <Stack align="center" gap="xs">
        <RingProgress sections={sections} startAngle={0} />
        <Text size="sm">0° (right)</Text>
      </Stack>

      <Stack align="center" gap="xs">
        <RingProgress sections={sections} startAngle={90} />
        <Text size="sm">90° (bottom)</Text>
      </Stack>

      <Stack align="center" gap="xs">
        <RingProgress sections={sections} startAngle={180} />
        <Text size="sm">180° (left)</Text>
      </Stack>

      <Stack align="center" gap="xs">
        <RingProgress sections={sections} startAngle={270} />
        <Text size="sm">270° (top)</Text>
      </Stack>
    </Group>
  );
}

Background color

Use the rootColor prop to customize the color of the unfilled portion of the ring (the background):

import { RingProgress } from '@mantine/core';

function Demo() {
  return <RingProgress sections={[{ value: 40, color: 'yellow' }]} rootColor="red" />;
}

Section events

Each section can receive any valid SVG <circle> element props, including event handlers like onClick, onMouseEnter, and onMouseLeave:

Hovered section: none

import { useState } from 'react';
import { RingProgress, Text } from '@mantine/core';

function Demo() {
  const [hovered, setHovered] = useState(-1);
  const reset = () => setHovered(-1);
  return (
    <>
      <RingProgress
        onMouseLeave={() => setHovered(-1)}
        size={140}
        sections={[
          { value: 40, color: 'cyan', onMouseEnter: () => setHovered(0), onMouseLeave: reset },
          { value: 20, color: 'blue', onMouseEnter: () => setHovered(1), onMouseLeave: reset },
          { value: 15, color: 'indigo', onMouseEnter: () => setHovered(2), onMouseLeave: reset },
        ]}
      />
      <Text>Hovered section: {hovered === -1 ? 'none' : hovered}</Text>
    </>
  );
}

Customize label

You can add any React node as a label, for example a Text component with custom styling or an ActionIcon:

40%

import { ActionIcon, RingProgress, Text, Center } from '@mantine/core';
import { CheckIcon } from '@phosphor-icons/react';

function Demo() {
  return (
    <>
      <RingProgress
        sections={[{ value: 40, color: 'blue' }]}
        label={
          <Text c="blue" fw={700} ta="center" size="xl">
            40%
          </Text>
        }
      />

      <RingProgress
        sections={[{ value: 100, color: 'teal' }]}
        label={
          <Center>
            <ActionIcon color="teal" variant="light" radius="xl" size="xl">
              <CheckIcon size={22} />
            </ActionIcon>
          </Center>
        }
      />
    </>
  );
}

Filled segment transition

By default, transitions are disabled. To enable them, set the transitionDuration prop to a number of milliseconds:

30%

import { useState } from 'react';
import { Button, RingProgress, Stack, Text } from '@mantine/core';

function Demo() {
  const [value, setValue] = useState(30);

  return (
    <Stack align="center">
      <RingProgress
        sections={[{ value, color: 'blue' }]}
        transitionDuration={250}
        label={<Text ta="center">{value}%</Text>}
      />

      <Button onClick={() => setValue(Math.floor(Math.random() * 100))}>Set random value</Button>
    </Stack>
  );
}