Charts & Statsdataanalyticschartdonutpie
Donut Chart
An interactive donut chart with percentage breakdown and glowing segments.
Live Preview
Loading Chart...
Usage
Copy and paste this code into your React component:
tsx
import { DonutChart } from 'react-component-kit-ui/charts';
<DonutChart title="Device Share" labels={labels} series={series} centerLabel="Devices" />Manual Installation
If you are not using the npm package, create a new file and paste the following code: src/components/DonutChart.tsx + DonutChart.css:
DonutChart.tsx
'use client';
import { useEffect, useRef } from 'react';
import './DonutChart.css';
/**
* DonutChart โ A minimalist donut chart component.
*/
export interface DonutChartProps {
title?: string;
labels?: string[];
series?: number[];
height?: number | string;
colors?: string[];
centerLabel?: string;
pie?: boolean;
className?: string;
}
export function DonutChart({
title,
labels = ['Direct', 'Social', 'Referral'],
series = [44, 33, 23],
height = 350,
colors = ['var(--c-accent, #b8860b)', '#3b82f6', '#10b981', '#f59e0b', '#ef4444'],
centerLabel = 'Total',
pie = false,
className = '',
}: DonutChartProps) {
const chartConfig = {
labels,
series,
height,
colors,
centerLabel,
pie,
};
// Ported from the Astro <script>: runs once after mount (DOM logic kept as in the Astro kit).
const mounted = useRef(false);
useEffect(() => {
if (mounted.current) return;
mounted.current = true;
async function initCharts() {
const charts = document.querySelectorAll('.premium-chart-card[data-chart-type="donut"]');
if (charts.length === 0) return;
let ApexChartsModule;
try {
ApexChartsModule = await import('apexcharts');
} catch (e) {
console.error('ApexCharts failed to load:', e);
return;
}
const ApexCharts = ApexChartsModule.default || ApexChartsModule;
charts.forEach(container => {
if (container.getAttribute('data-initialized')) return;
const configStr = container.getAttribute('data-config');
if (!configStr) return;
const config = JSON.parse(configStr);
config.colors = (config.colors || []).map((c: string) => {
const m = /^var\((--[\w-]+)\s*(?:,\s*([^)]+))?\)$/.exec(String(c).trim());
return m ? getComputedStyle(container as Element).getPropertyValue(m[1]).trim() || (m[2] || '').trim() : c;
});
const target = container.querySelector('.chart-container');
const fallback = container.querySelector('.chart-fallback');
if (!target) return;
const options = {
series: config.series,
labels: config.labels,
chart: {
type: config.pie ? 'pie' : 'donut',
height: config.height,
background: 'transparent',
fontFamily: 'Inter, sans-serif',
},
colors: config.colors,
stroke: { show: false },
dataLabels: { enabled: false },
plotOptions: {
pie: {
donut: {
size: '75%',
labels: {
show: !config.pie,
name: { show: true, color: '#94a3b8', fontSize: '14px' },
value: { show: true, color: '#ffffff', fontSize: '24px', fontWeight: 800 },
total: {
show: true,
label: config.centerLabel,
color: '#94a3b8',
formatter: (w: any) => w.globals.seriesTotals.reduce((a: any, b: any) => a + b, 0),
},
},
},
},
},
legend: {
show: true,
position: 'bottom',
labels: { colors: '#94a3b8' },
},
tooltip: { theme: 'dark' },
theme: { mode: 'dark' }
};
try {
const chart = new ApexCharts(target, options);
chart.render().then(() => {
if (fallback) (fallback as HTMLElement).style.display = 'none';
});
container.setAttribute('data-initialized', 'true');
} catch (err) {
console.error('Failed to render chart:', err);
if (fallback) fallback.textContent = 'Failed to load chart';
}
});
}
initCharts();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<>
<div className={`premium-chart-card ${className}`} data-chart-type="donut" data-config={JSON.stringify(chartConfig)} data-rk-ab60a1="">
{title && (
<div className="premium-chart-card__header" data-rk-ab60a1="">
<h3 className="premium-chart-card__title" data-rk-ab60a1="">{title}</h3>
</div>
)}
<div className="premium-chart-card__body" data-rk-ab60a1="">
<div className="chart-container" data-rk-ab60a1=""></div>
<div className="chart-fallback" data-rk-ab60a1="">Loading Chart...</div>
</div>
</div>
</>
);
}
export default DonutChart;DonutChart.css
/* DonutChart โ ported from astro-component-kit (charts/DonutChart.astro) */
.premium-chart-card:where([data-rk-ab60a1]) {
background: rgb(var(--ink-rgb, 255 255 255) / 0.03);
backdrop-filter: blur(12px);
border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.08);
border-radius: var(--r-xl, 24px);
padding: var(--sp-6, 24px);
width: 100%;
min-width: 300px;
}
.premium-chart-card__header:where([data-rk-ab60a1]) {
margin-bottom: var(--sp-6, 24px);
}
.premium-chart-card__title:where([data-rk-ab60a1]) {
font-size: 1.125rem;
font-weight: 700;
color: var(--c-text-1, #ffffff);
margin: 0;
}
.premium-chart-card__body:where([data-rk-ab60a1]) {
width: 100%;
position: relative;
min-height: 200px;
}
.chart-container:where([data-rk-ab60a1]) {
width: 100%;
position: relative;
z-index: 2;
}
.chart-fallback:where([data-rk-ab60a1]) {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.8rem;
color: var(--c-text-3, #64748b);
z-index: 1;
}Quick Info
- Category
- Charts & Stats
- Filename
DonutChart.tsx- Dependencies
- None โ pure React + CSS
- Tags
- dataanalyticschartdonutpie