{"schemaVersion":"1.0","type":"Article","slug":"mantine-rings-progress-a-complete-rewrite-inspired-by-apple-watch-d0l9s","url":"https://api.zyvop.com/mantine-rings-progress-a-complete-rewrite-inspired-by-apple-watch-d0l9s","title":"Mantine Rings Progress - A Complete Rewrite Inspired by Apple Watch","subtitle":null,"tldr":"The concentric ring progress component for Mantine gets a major upgrade: native RingProgress, glow...","keywords":["webdev","Programming","JavaScript","react"],"entities":["Giovambattista Fazioli","Senior Full-stack Engineer, Lead Developer","webdev","Programming","JavaScript","react","ZyVOP"],"keyTakeaways":["The concentric ring progress component for Mantine gets a major upgrade: native RingProgress, glow effects, staggered animations, unified tooltips, and full accessibility — all in one release.","Introduction Version 3.0.0 of @gfazioli/mantine-rings-progress is a ground-up rewrite.","The internal fork of Mantine's RingProgress has been completely removed, replaced by the native component from @mantine/core."],"headings":["Introduction","✨ New Features","Per-ring Customization","Staggered Entrance Animation","Glow / Neon Effect","Pulse on Completion","onRingComplete Callback","Start Angle &amp; Direction","Unified Tooltip","Accessibility","💥 Breaking Changes","Animation props renamed/removed","Ring type changed","Props interface","Styles API","🔧 Improvements","Drastically simplified internals","Smart entrance animation","Interactive demos","🐛 Bug Fixes","Getting Started","Links"],"outboundLinks":["https://gfazioli.github.io/mantine-rings-progress/","https://www.npmjs.com/package/@gfazioli/mantine-rings-progress","https://github.com/gfazioli/mantine-rings-progress","https://mantine-extensions.vercel.app/"],"contentText":"The concentric ring progress component for Mantine gets a major upgrade: native RingProgress, glow effects, staggered animations, unified tooltips, and full accessibility — all in one release. Introduction Version 3.0.0 of @gfazioli/mantine-rings-progress is a ground-up rewrite. The internal fork of Mantine's RingProgress has been completely removed, replaced by the native component from @mantine/core. This eliminates ~600 lines of custom SVG and animation code while adding 10 new features that bring the component closer to the Apple Watch activity rings experience. Whether you're building fitness dashboards, goal trackers, or compact data visualizations, this release gives you the tools to make your rings shine — literally. ✨ New Features Per-ring Customization Each ring can now override global props individually. Mix thick and thin rings, toggle round caps per ring, or set a custom background track color: const rings = [{ value: 40, color: 'cyan', thickness: 18 },{ value: 65, color: 'red', thickness: 10, roundCaps: false },{ value: 90, color: '#f90', thickness: 6, rootColor: 'gray' },]; &lt;RingsProgress size={180} rings={rings} /&gt; Enter fullscreen mode Exit fullscreen mode Supported per-ring overrides: thickness, roundCaps, rootColor, glowIntensity, glowColor, ariaLabel, tooltip. Staggered Entrance Animation Instead of all rings appearing at once, use staggerDelay to animate them one after another — outer ring first, inner ring last: &lt;RingsProgress animate staggerDelay={300} transitionDuration={1000} rings={rings} /&gt; Enter fullscreen mode Exit fullscreen mode Glow / Neon Effect Add a neon-like glow that follows the ring shape using CSS drop-shadow. Set glow={true} for a default 6px blur, or pass a custom radius. Each ring can override the effect with glowIntensity and glowColor: &lt;RingsProgress glow={8} rings={rings} /&gt; Enter fullscreen mode Exit fullscreen mode Pulse on Completion When a ring reaches 100%, trigger a satisfying scale + brightness pulse animation — inspired by the Apple Watch ring closure celebration: &lt;RingsProgress pulseOnComplete rings={rings} /&gt; Enter fullscreen mode Exit fullscreen mode The animation respects prefers-reduced-motion and uses a data-pulsing attribute for CSS customization. onRingComplete Callback Run custom logic when a ring crosses the 100% threshold: &lt;RingsProgressonRingComplete={(index, ring) =&gt; { showNotification({ message: `${ring.color} ring completed!` });}}rings={rings}/&gt; Enter fullscreen mode Exit fullscreen mode Start Angle &amp; Direction Control where rings start filling and in which direction: &lt;RingsProgress startAngle={90} direction=\"counterclockwise\" rings={rings} /&gt; Enter fullscreen mode Exit fullscreen mode startAngle={0} is the 12 o'clock position (default). Direction accepts 'clockwise' or 'counterclockwise'. Unified Tooltip The new withTooltip prop shows a single, chart-style tooltip with color swatches for all rings — no more overlapping per-ring tooltips: &lt;RingsProgresswithTooltiprings={[ { value: 20, color: 'green', tooltip: 'Fitness – 40 Gb' }, { value: 80, color: 'blue', tooltip: 'Running – 50 min' },]}/&gt; Enter fullscreen mode Exit fullscreen mode If tooltip is not set on a ring, the percentage value is shown automatically. Accessibility The component is now fully accessible out of the box: Root element: role=\"group\" with aria-label=\"Progress rings\" (overridable) Each ring: role=\"progressbar\" with aria-valuenow, aria-valuemin, aria-valuemax Per-ring ariaLabel for custom screen reader text Automatic prefers-reduced-motion support via useReducedMotion 💥 Breaking Changes This is a major release with breaking changes. Here's what you need to update: Animation props renamed/removed &lt;RingsProgress - animationDuration={1000} - animationSteps={60} - animationTimingFunction=\"ease\" + transitionDuration={1000} animate rings={rings} /&gt; Enter fullscreen mode Exit fullscreen mode animationDuration → transitionDuration (default changed from 1000 to 0) animationSteps — removed (CSS transitions replaced JS setInterval) animationTimingFunction — removed (CSS uses ease by default) Tip: If you only use animate for entrance animation, you can omit transitionDuration entirely — the component auto-applies 1000ms during the entrance phase. Ring type changed - import { RingProgressSection } from '@gfazioli/mantine-rings-progress'; - const rings: RingProgressSection[] = [...] + import { RingsProgressRing } from '@gfazioli/mantine-rings-progress'; + const rings: RingsProgressRing[] = [...] Enter fullscreen mode Exit fullscreen mode The base properties (value, color, tooltip) remain the same, so existing code that only uses those will work without changes. Props interface RingsProgressProps now extends BoxProps + StylesApiProps + ElementProps&lt;'div'&gt; (standard Mantine pattern) instead of the forked RingProgressProps. All common props (size, thickness, roundCaps, label, animate) are still available. Styles API A new 'label' style name was added. Style names are now 'root' | 'ring' | 'label'. 🔧 Improvements Drastically simplified internals The entire forked RingProgress/ directory (~600 lines) has been deleted. The component now delegates entirely to Mantine's native RingProgress, ensuring better compatibility with future Mantine releases and smaller bundle size. Smart entrance animation The component auto-applies 1000ms transition during the entrance phase only. After all rings mount, transitionDuration reverts to 0 — preventing geometry changes (like resizing) from triggering unwanted CSS transitions. Interactive demos The documentation now includes replay buttons for animation demos, interactive sliders for the pulse demo, and a configurator with all new props. 🐛 Bug Fixes Label centering: Fixed the label being offset from center — now rendered as a separate overlay Debug styles: Removed hardcoded color: 'red' on the label Theme overrides: Fixed useProps('Rings', ...) → useProps('RingsProgress', ...) Countdown demo: Added transitionDuration={0} to prevent CSS transitions from interfering with rapid updates Getting Started npm install @gfazioli/mantine-rings-progress@3 Enter fullscreen mode Exit fullscreen mode Import styles at the root of your application: import '@gfazioli/mantine-rings-progress/styles.css'; Enter fullscreen mode Exit fullscreen mode Basic usage with the new features: import { RingsProgress } from '@gfazioli/mantine-rings-progress'; function ActivityRings() {const rings = [ { value: 75, color: 'green', tooltip: 'Move' }, { value: 50, color: 'blue', tooltip: 'Exercise' }, { value: 90, color: 'orange', tooltip: 'Stand' },]; return ( &lt;RingsProgress size={180} rings={rings} animate staggerDelay={300} glow={6} withTooltip pulseOnComplete onRingComplete={(i) =&gt; console.log(`Ring ${i} done!`)} /&gt;);} Enter fullscreen mode Exit fullscreen mode Links 📦 npm package 📖 Documentation &amp; Demos 🔗 GitHub Repository 🧩 Mantine Extensions Hub","contentHash":"sha256:b61089cd19c4547221265f5da17ababe281682fe6ec083763cbd975f806d8d94","authorName":"Giovambattista Fazioli","authorUrl":"https://api.zyvop.com/author/giovambattista","authorSameAs":["https://gfazioli.github.io","https://github.com/gfazioli","https://x.com/gfazioli","https://www.linkedin.com/in/giovambattistafazioli/"],"category":null,"tags":["webdev","Programming","JavaScript","react"],"audience":"Readers researching webdev","tone":"Professional, senior full-stack engineer, lead developer perspective","readingTimeMinutes":4,"wordCount":910,"faqs":null,"primaryTopic":"webdev","publishedAt":"2026-09-04T09:25:20.082Z","updatedAt":"2026-09-04T09:25:20.082Z","canonicalUrl":"https://dev.to/undolog/mantine-rings-progress-a-complete-rewrite-inspired-by-apple-watch-1nlp"}