Ring Chart
Concentric ring chart for multi-metric progress and comparison visualization
Installation
Dependencies
Examples
Fitness
"use client"
import RingChart, { Ring, RingCenter } from "@/components/vritti/ring-chart"
const data = [
{ label: "Steps", value: 8420, maxValue: 10000, color: "var(--chart-1)" },
{ label: "Calories", value: 1840, maxValue: 2200, color: "var(--chart-2)" },
{ label: "Active Min", value: 38, maxValue: 60, color: "var(--chart-3)" },
{ label: "Sleep (h)", value: 7.2, maxValue: 8, color: "var(--chart-4)" },
]
export function RingChartFitnessDemo() {
return (
<div className="flex justify-center p-8">
<RingChart data={data} size={280}>
{data.map((item, index) => (
<Ring index={index} key={item.label} />
))}
<RingCenter
defaultLabel="Today"
formatOptions={{ maximumFractionDigits: 1 }}
/>
</RingChart>
</div>
)
}
Legend
Sessions by Channel
Organic4,25085%
Paid3,12062%
Email2,10042%
Social1,58032%
Referral1,05021%
Direct74715%
"use client"
import { useState } from "react"
import RingChart, {
Legend,
LegendItem,
LegendLabel,
LegendMarker,
LegendProgress,
LegendValue,
Ring,
RingCenter,
type LegendItemData,
type RingData,
} from "@/components/vritti/ring-chart"
const sessionsData: RingData[] = [
{ label: "Organic", value: 4250, maxValue: 5000, color: "#0ea5e9" },
{ label: "Paid", value: 3120, maxValue: 5000, color: "#a855f7" },
{ label: "Email", value: 2100, maxValue: 5000, color: "#f59e0b" },
{ label: "Social", value: 1580, maxValue: 5000, color: "#10b981" },
{ label: "Referral", value: 1050, maxValue: 5000, color: "#ef4444" },
{ label: "Direct", value: 747, maxValue: 5000, color: "#6366f1" },
]
const legendItems: LegendItemData[] = sessionsData.map((d) => ({
label: d.label,
value: d.value,
maxValue: d.maxValue,
color: d.color ?? "",
}))
export function RingChartLegendDemo() {
const [hoveredIndex, setHoveredIndex] = useState<number | null>(null)
return (
<div className="flex flex-col items-center justify-center gap-8 p-4 lg:flex-row lg:gap-12">
<RingChart
data={sessionsData}
hoveredIndex={hoveredIndex}
onHoverChange={setHoveredIndex}
size={240}
>
{sessionsData.map((item, index) => (
<Ring index={index} key={item.label} />
))}
<RingCenter defaultLabel="Total Sessions" />
</RingChart>
<Legend
hoveredIndex={hoveredIndex}
items={legendItems}
onHoverChange={setHoveredIndex}
title="Sessions by Channel"
>
<LegendItem className="grid grid-cols-[auto_1fr_auto] items-center gap-x-3 gap-y-1">
<LegendMarker />
<LegendLabel />
<LegendValue showPercentage />
<div className="col-span-full">
<LegendProgress />
</div>
</LegendItem>
</Legend>
</div>
)
}
Progress
"use client"
import RingChart, { Ring } from "@/components/vritti/ring-chart"
const data = [
{ label: "Q1 Target", value: 75, maxValue: 100, color: "hsl(217, 91%, 60%)" },
{ label: "Q2 Target", value: 50, maxValue: 100, color: "hsl(280, 87%, 65%)" },
{ label: "Q3 Target", value: 90, maxValue: 100, color: "hsl(142, 71%, 45%)" },
]
export function RingChartProgressDemo() {
return (
<div className="flex justify-center p-8">
<RingChart data={data} size={260} strokeWidth={16} ringGap={8}>
{data.map((item, index) => (
<Ring index={index} key={item.label} />
))}
</RingChart>
</div>
)
}