Skip to content
Template Empire
Browse components

Progress Ring Stats

A free logo and stats section for React: circular progress rings that sweep to their value on scroll.

Rollout results

What changed after teams automated the release path

Median across 412 engineering teams, measured in production.

Deploy time cut: 44 percent. 22 min median, down from 39
Rollbacks avoided: 61 percent. One release in nine went back. Now one in 23.
Alerts closed first-touch: 78 percent. No escalation, no second responder
Services on auto-rollback: 89 percent. 190 of 214

Customise

160px
1.2s

Code

const stats = [
  {
    label: "Deploy time cut",
    value: 44,
    suffix: "%",
    detail: "22 min median, down from 39",
  },
  {
    label: "Services on auto-rollback",
    value: 89,
    suffix: "%",
    detail: "190 of 214",
  },
];

<StatProgressRing stats={stats} />

Progress Ring Stats is a free animated stats section for Next.js and React, styled with Tailwind. It renders circular progress stats as SVG rings: the first time the section scrolls into view, every ring sweeps from empty to its percentage with Motion while the number in its centre counts up to match, rings starting 100 milliseconds apart.

The final number and the fully drawn ring of every stat are rendered in the server HTML, so crawlers, no-JS visitors and reduced-motion users always see the real values (enhancement-only animation). The sweep writes the stroke offset and the count writes into the DOM through a single Motion value subscription with zero React re-renders per frame, tabular numerals and a reserved width hold layout perfectly stable, and screen readers only ever hear each ring's finished figure: its label, its final value and its detail line where the stat has one.

Install

Add the @te registry to your components.json once:

{
  "registries": {
    "@te": "https://templateempire.io/r/{name}.json"
  }
}

Then install the component:

npx shadcn@latest add @te/stat-progress-ring

Usage

import { StatProgressRing } from "@/components/stat-progress-ring";

const stats = [
  {
    label: "Deploy time cut",
    value: 44,
    suffix: "%",
    detail: "22 min median, down from 39",
  },
  {
    label: "Rollbacks avoided",
    value: 61,
    suffix: "%",
    detail: "One release in nine went back. Now one in 23.",
  },
  {
    label: "Alerts closed first-touch",
    value: 78,
    suffix: "%",
    detail: "No escalation, no second responder",
  },
  {
    label: "Services on auto-rollback",
    value: 89,
    suffix: "%",
    detail: "190 of 214",
  },
];

export function Example() {
  return <StatProgressRing stats={stats} />;
}

Props

PropTypeDefaultDescription
stats{ label: string; value: number; suffix?: string; detail?: string }[]required2 to 6 stats; each value is a percent from 0 to 100, rendered final in the SSR HTML. detail is the optional claim line under the label, spoken after the value.
sizenumber160Ring diameter in pixels; stroke width and radius derive from it.
durationnumber1.2Sweep and count-up duration in seconds per ring (--dur-4).
classNamestringundefinedAdditional classes merged onto the section.

Customisation

Three CSS variables colour the rings, and all three default to your theme tokens: --stat-ring-track for the unfilled track (--border), and --stat-ring-sweep-from and --stat-ring-sweep-to for the two stops of the gradient the swept arc is painted with (--primary and the accent token). Set them at :root or on any ancestor to run the sweep in a different pair. Shape each stat through its own entry, adding detail for the claim line under the label, and tune the section with the props: size sets the ring diameter, duration paces the sweep against your motion system, and className restyles the grid, for example to change the gap or column widths:

<StatProgressRing
  stats={stats}
  size={200}
  duration={2}
  className="max-w-4xl gap-y-14"
/>

/* in your stylesheet */
:root {
  --stat-ring-sweep-from: var(--primary);
  --stat-ring-sweep-to: var(--ring);
}

Frequently asked questions

Is Progress Ring Stats free for commercial use?
Yes. Progress Ring Stats is MIT licensed, so you can use it in personal and commercial projects without attribution.
Do the rings hurt SEO or accessibility?
No. The server HTML contains every final number and a fully drawn ring, so crawlers and no-JS visitors read the real values without JavaScript. Each ring exposes one visually hidden string to assistive technology, carrying its label, its final value and, when the stat supplies one, its detail line, while the SVG and the visible number, label and detail are hidden from it, so screen readers never hear an intermediate frame. With reduced motion enabled nothing sweeps or counts and the final state renders immediately.
How do I change the ring size or the number suffix?
Set the size prop for the ring diameter; the stroke width and radius scale with it. Add a suffix such as a percent sign on each stat to render it after the counting number. Values are percentages from 0 to 100 and the count keeps the same rounding as the final number on every frame.

Related components

Want the full page this belongs to?Template Empire builds complete Next.js templates with this motion system throughout. Browse the paid templates that pair with these free components.Explore Template Empire templates