CircularProgress

Renders a circular loading indicator. Supports indeterminate (looping) and determinate (value-driven) variants. The stroke color is set via CSS currentColor; the color prop resolves to theme.palette[color].main.

Architecture: the root element is a <span> (not <svg>). This avoids a TypeScript incompatibility between SVGAttributes.height and LayoutProps.height. The internal <svg> fills the span via width/height="100%" and uses a fixed viewBox="0 0 44 44" coordinate system - the size prop scales the outer span via CSS width/height.

Examples

Default

<CircularProgress variant="indeterminate" color="primary" size={40} />

Determinate

<CircularProgress variant="determinate" color="primary" size={40} value={70} />

Spinning Determinate

25%
50%
75%
<Flex flexDirection="row" gap={4} alignItems="center">
  {([25, 50, 75] as const).map((value) => (
    <Flex key={value} flexDirection="column" alignItems="center" gap={1}>
      <CircularProgress variant="determinate" value={value} spinning size={48} />
      <Typography variant="caption" color="secondary" m={0}>
        {value}%
      </Typography>
    </Flex>
  ))}
</Flex>

Colors

primary
secondary
success
error
info
warning
<Flex flexDirection="column" gap={3}>
  {(['primary', 'secondary', 'success', 'error', 'info', 'warning'] as const).map((color) => (
    <Flex key={color} flexDirection="row" gap={2} alignItems="center">
      <Typography variant="caption" color="secondary" width="6rem" flexShrink={0} m={0}>
        {color}
      </Typography>
      <CircularProgress showTrack color={color} size={32} />
      <CircularProgress showTrack variant="determinate" value={65} color={color} size={32} />
    </Flex>
  ))}
</Flex>

Sizes

16px
24px
40px
64px
80px
<Flex flexDirection="row" gap={3} alignItems="end" flexWrap="wrap">
  {([16, 24, 40, 64, 80] as const).map((size) => (
    <Flex key={size} flexDirection="column" alignItems="center" gap={1}>
      <CircularProgress size={size} />
      <Typography variant="caption" color="secondary" m={0}>
        {size}px
      </Typography>
    </Flex>
  ))}
</Flex>

Thickness

1
2
3.6
6
10
<Flex flexDirection="row" gap={3} alignItems="center" flexWrap="wrap">
  {([1, 2, 3.6, 6, 10] as const).map((thickness) => (
    <Flex key={thickness} flexDirection="column" alignItems="center" gap={1}>
      <CircularProgress thickness={thickness} size={48} />
      <Typography variant="caption" color="secondary" m={0}>
        {thickness}
      </Typography>
    </Flex>
  ))}
</Flex>

Show Track

indeterminate
determinate 65%
<Flex flexDirection="row" gap={4} alignItems="center" flexWrap="wrap">
  <Flex flexDirection="column" alignItems="center" gap={1}>
    <CircularProgress showTrack size={48} />
    <Typography variant="caption" color="secondary" m={0}>
      indeterminate
    </Typography>
  </Flex>
  <Flex flexDirection="column" alignItems="center" gap={1}>
    <CircularProgress variant="determinate" value={65} showTrack size={48} />
    <Typography variant="caption" color="secondary" m={0}>
      determinate 65%
    </Typography>
  </Flex>
</Flex>

Easing

linear
ease
ease-in
ease-out
ease-in-out
<Flex flexDirection="row" gap={4} alignItems="center" flexWrap="wrap">
  {(['linear', 'ease', 'ease-in', 'ease-out', 'ease-in-out'] as const).map((easing) => (
    <Flex key={easing} flexDirection="column" alignItems="center" gap={1}>
      <CircularProgress easing={easing} size={48} />
      <Typography variant="caption" color="secondary" m={0}>
        {easing}
      </Typography>
    </Flex>
  ))}
</Flex>

Custom Range

3 of 10
120 of 200
7 of 5-15
<Flex flexDirection="column" gap={3}>
  {(
    [
      { value: 3, min: 0, max: 10, label: '3 of 10' },
      { value: 120, min: 0, max: 200, label: '120 of 200' },
      { value: 7, min: 5, max: 15, label: '7 of 5-15' },
    ] as const
  ).map(({ value, min, max, label }) => (
    <Flex key={label} flexDirection="row" gap={2} alignItems="center">
      <CircularProgress
        variant="determinate"
        value={value}
        min={min}
        max={max}
        showTrack
        size={48}
      />
      <Typography variant="caption" color="secondary" m={0}>
        {label}
      </Typography>
    </Flex>
  ))}
</Flex>

Spinning With Track

25%
50%
75%
90%
<Flex flexDirection="row" gap={4} alignItems="center" flexWrap="wrap">
  {([25, 50, 75, 90] as const).map((value) => (
    <Flex key={value} flexDirection="column" alignItems="center" gap={1}>
      <CircularProgress
        variant="determinate"
        value={value}
        spinning
        showTrack
        size={56}
        thickness={5}
      />
      <Typography variant="caption" color="secondary" m={0}>
        {value}%
      </Typography>
    </Flex>
  ))}
</Flex>

Disable Shrink

default
disableShrink
<Flex flexDirection="row" gap={4} alignItems="center">
  <Flex flexDirection="column" alignItems="center" gap={1}>
    <CircularProgress />
    <Typography variant="caption" color="secondary" m={0}>
      default
    </Typography>
  </Flex>
  <Flex flexDirection="column" alignItems="center" gap={1}>
    <CircularProgress disableShrink />
    <Typography variant="caption" color="secondary" m={0}>
      disableShrink
    </Typography>
  </Flex>
</Flex>

CircularProgress API reference