1 GitHub star = 1 new component.

Star the repo
Charts & Statschartsvgzero-dependencygaugekpigoalprogress

Gauge Chart

Semicircular progress gauge for a single KPI against a goal with the number always shown as text.

Live Preview

Monthly sales goal

November

$73.4k73% of $100k goal
View data
MetricValueGoal% of goal
Monthly sales goal$73.4k$100k73.4%

Usage

Copy and paste this code into your React component:

tsx
import { GaugeChart } from 'react-component-kit-ui/charts';

<GaugeChart title="Sales goal" value={73400} max={100000} prefix="$" label="of $100k goal" />

Manual Installation

If you are not using the npm package, create a new file and paste the following code: src/components/GaugeChart.tsx + GaugeChart.css:

GaugeChart.tsx
'use client';

import ChartFrame from './ChartFrame';
import { arcStroke, formatValue } from './chart-utils';
import './GaugeChart.css';

/**
 * GaugeChart โ€” Semicircular progress gauge for a single KPI against a goal,
 * with the number and % of goal always shown as text.
 *
 * @param {number} value - Current value.
 * @param {number} max - Goal / maximum.
 * @param {string} label - Caption under the number.
 * @param {string} prefix - Value prefix.
 * @param {string} suffix - Value suffix.
 * @param {string} color - Arc color.
 * @param {string} variant - Card style (glass|solid|ghost).
 * @param {string} size - Density (sm|md|lg).
 */
export interface GaugeChartProps {
  title?: string;
  subtitle?: string;
  value?: number;
  max?: number;
  label?: string;
  prefix?: string;
  suffix?: string;
  color?: string;
  variant?: 'glass' | 'solid' | 'ghost';
  size?: 'sm' | 'md' | 'lg';
  className?: string;
}

export function GaugeChart({
  title = 'Monthly sales goal',
  subtitle = 'November',
  value = 73400,
  max = 100000,
  label = 'of $100k goal',
  prefix = '$',
  suffix = '',
  color = 'var(--chart-1, #3987e5)',
  variant = 'glass',
  size = 'md',
  className = '',
}: GaugeChartProps) {
  const W = 260;
  const H = 150;
  const cx = W / 2;
  const cy = 132;
  const r = 108;
  const pct = Math.max(0, Math.min(1, value / (max || 1)));
  const a0 = -Math.PI / 2;
  const a1 = Math.PI / 2;
  const fmt = (v: number) => formatValue(v, prefix, suffix);
  return (
    <>
      <ChartFrame title={title} subtitle={subtitle} variant={variant} size={size} className={className} table={{ headers: ['Metric', 'Value', 'Goal', '% of goal'], rows: [[title, fmt(value), fmt(max), `${(pct * 100).toFixed(1)}%`]] }}>
        <div className="gc" role="meter" aria-valuemin={0} aria-valuemax={max} aria-valuenow={value} aria-label={`${title}: ${fmt(value)} ${label}, ${(pct * 100).toFixed(0)}%`} data-rk-f7ded1="">
          <svg viewBox={`0 0 ${W} ${H}`} aria-hidden="true" data-rk-f7ded1="">
            <path d={arcStroke(cx, cy, r, a0, a1)} fill="none" stroke="rgb(var(--ink-rgb, 255 255 255) / 0.07)" strokeWidth="18" strokeLinecap="round" data-rk-f7ded1="" />
            {pct > 0 && <path className="gc__arc" d={arcStroke(cx, cy, r, a0, a0 + Math.PI * pct)} fill="none" stroke={color} strokeWidth="18" strokeLinecap="round" data-rk-f7ded1="" />}
            <text className="cf-label" x={cx - r} y={H - 2} textAnchor="middle" data-rk-f7ded1="">0</text>
            <text className="cf-label" x={cx + r} y={H - 2} textAnchor="middle" data-rk-f7ded1="">{fmt(max)}</text>
          </svg>
          <div className="gc__center" data-rk-f7ded1="">
            <span className="gc__value" data-rk-f7ded1="">{fmt(value)}</span>
            <span className="gc__label" data-rk-f7ded1="">{(pct * 100).toFixed(0)}% {label}</span>
          </div>
        </div>
      </ChartFrame>
    </>
  );
}

export default GaugeChart;
GaugeChart.css
/* GaugeChart โ€” ported from astro-component-kit (charts/GaugeChart.astro) */

.gc:where([data-rk-f7ded1]) {
  position: relative;
  max-width: 340px;
  margin: 0 auto;
}
.gc__arc:where([data-rk-f7ded1]) {
  stroke-dasharray: 400;
  stroke-dashoffset: 400;
  animation: gauge-chart-gc-draw 1.1s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}
@keyframes gauge-chart-gc-draw { to { stroke-dashoffset: 0; } }
.gc__center:where([data-rk-f7ded1]) {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 6%;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.gc__value:where([data-rk-f7ded1]) {
  font-size: clamp(1.5rem, 5vw, 2rem);
  font-weight: 750;
  color: var(--c-text-1, #fff);
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}
.gc__label:where([data-rk-f7ded1]) { font-size: 0.75rem; color: var(--c-text-2, #a3a3a3); }
@media (prefers-reduced-motion: reduce) {
  .gc__arc:where([data-rk-f7ded1]) { animation: none; stroke-dashoffset: 0; }
}

Quick Info

Category
Charts & Stats
Filename
GaugeChart.tsx
Dependencies
None โ€” pure React + CSS
Tags
chartsvgzero-dependencygaugekpigoalprogress