import { GaugeChart } from "semiotic"
Quick Start
A gauge displays a single numeric value against a scale with optional threshold zones. Built on top of the ordinal frame's radial projection — the same rendering pipeline as pie and donut charts.
import { GaugeChart } from "semiotic" const frameProps = { /* --- Data --- */ value: 72, /* --- Realtime --- */ thresholds: [ { value: 50, color: "#4caf50", label: "Good" }, { value: 80, color: "#ff9800", label: "Warning" }, { value: 100, color: "#f44336", label: "Critical" }, ], /* --- Other --- */ max: 100 } export default () => { return <GaugeChart {...frameProps} /> }
Examples
Simple Gauge
A minimal gauge with no threshold zones — just a value against a scale.
import { GaugeChart } from "semiotic" const frameProps = { /* --- Data --- */ value: 65, /* --- Size --- */ width: 250, height: 200, /* --- Other --- */ max: 100 } export default () => { return <GaugeChart {...frameProps} /> }
Threshold Zones
Define color-coded zones with the thresholds prop. Each threshold specifies the upper bound of a zone. The gauge fills through zones as the value increases.
import { GaugeChart } from "semiotic" const frameProps = { /* --- Data --- */ value: 35, /* --- Size --- */ width: 250, height: 200, /* --- Realtime --- */ thresholds: [ { value: 25, color: "#f44336" }, { value: 50, color: "#ff9800" }, { value: 75, color: "#ffeb3b" }, { value: 100, color: "#4caf50" }, ], /* --- Other --- */ min: 0, max: 100 } export default () => { return <GaugeChart {...frameProps} /> }
Custom Range
Gauges don't have to be 0–100. Set min and max to any range, and use valueFormat for custom labels.
import { GaugeChart } from "semiotic" const frameProps = { /* --- Data --- */ value: 4.2, /* --- Size --- */ width: 250, height: 200, /* --- Customize --- */ valueFormat: v => `${v.toFixed(1)} / 5`, /* --- Realtime --- */ thresholds: [{ value: 2, color: "#f44336" }, { value: 3.5, color: "#ff9800" }, ... ], /* --- Other --- */ min: 0, max: 5 } export default () => { return <GaugeChart {...frameProps} /> }
Half Circle
Set sweep=180 for a traditional half-circle gauge.
import { GaugeChart } from "semiotic" const frameProps = { /* --- Data --- */ value: 80, /* --- Size --- */ width: 300, height: 180, /* --- Realtime --- */ thresholds: [{ value: 60, color: "#4caf50" }, { value: 80, color: "#ff9800" }, ... ], /* --- Other --- */ sweep: 180 } export default () => { return <GaugeChart {...frameProps} /> }
Rounded Ends
Pass cornerRadius (pixels) to round the gauge's outer endpoints — the start of the first zone and the end of the last zone. Internal zone boundaries stay square so threshold seams remain visually distinct (mirrors the swimlane convention for radial sectors).
import { GaugeChart } from "semiotic" const frameProps = { /* --- Data --- */ value: 72, /* --- Size --- */ width: 280, height: 220, /* --- Realtime --- */ thresholds: [ { value: 50, color: "#4caf50", label: "Good" }, { value: 80, color: "#ff9800", label: "Warning" }, { value: 100, color: "#f44336", label: "Critical" }, ], /* --- Other --- */ cornerRadius: 10, arcWidth: 0.35 } export default () => { return <GaugeChart {...frameProps} /> }
Arc Gradient
Use gradientFill to sample colors along the gauge arc. The gradient is applied over the gauge track, so this example hides the needle and shows a rounded 70% gradient over the full grey arc.
import { GaugeChart } from "semiotic" const frameProps = { /* --- Data --- */ value: 70, /* --- Size --- */ width: 280, height: 220, /* --- Customize --- */ gradientFill: { colorStops: [ { offset: 0, color: "#2782d7" }, { offset: 0.3, color: "#f4ee43" }, { offset: 0.7, color: "#fdd961" }, { offset: 1, color: "#ca2020" }, ] }, /* --- Other --- */ min: 0, max: 100, fillZones: true, showNeedle: false, backgroundColor: "#d1d5db", cornerRadius: 50 } export default () => { return <GaugeChart {...frameProps} /> }
Thick Arc
Increase arcWidth for a chunkier ring. Decrease for a thin progress ring.
import { GaugeChart } from "semiotic" const frameProps = { /* --- Data --- */ value: 55, /* --- Size --- */ width: 200, height: 200, /* --- Other --- */ arcWidth: 0.5, showNeedle: false } export default () => { return <GaugeChart {...frameProps} /> }
Custom Center Content
Replace the default value label with any React content via centerContent.
JSX
<GaugeChart value={92} thresholds={[ { value: 90, color: "#f44336" }, { value: 95, color: "#ff9800" }, { value: 100, color: "#4caf50" }, ]} centerContent={(value, min, max) => ( <div style={{ textAlign: "center" }}> <div style={{ fontSize: 28, fontWeight: 700 }}>{value}%</div> <div style={{ fontSize: 11, color: "#888" }}>Uptime SLA</div> </div> )} />
Streaming — Election Needle
The gauge below simulates a live election forecast in the style of the NYT needle. The value drifts around 50% with a slight lean, updating every 400ms. The diverging color scheme runs from red (Candidate A) through a grey toss-up zone to blue (Candidate B). Only the needle moves — the threshold zones stay fixed.
Estimated chance of winning
← Candidate ACandidate B →
JSX
function ElectionNeedle() { const [value, setValue] = useState(50) useEffect(() => { const id = setInterval(() => { setValue(v => { const drift = (Math.random() - 0.48) * 3 const revert = (52 - v) * 0.02 return Math.max(40, Math.min(60, v + drift + revert)) }) }, 400) return () => clearInterval(id) }, []) return ( <GaugeChart value={value} min={40} max={60} sweep={180} arcWidth={0.15} needleColor="#222" fillZones={false} // zones stay fixed, only needle moves thresholds={[ { value: 45, color: "#d73027" }, // strong A (dark red) { value: 48, color: "#fc8d59" }, // lean A (light red) { value: 52, color: "#ccc" }, // toss-up (grey) { value: 55, color: "#91bfdb" }, // lean B (light blue) { value: 60, color: "#4575b4" }, // strong B (dark blue) ]} centerContent={...} // +{abs(value-50)} colored by leader /> ) }
Props
| Prop | Type | Required | Default | Description |
|---|
value | number | Yes | — | Current gauge value. |
min | number | — | 0 | Minimum scale value. |
max | number | — | 100 | Maximum scale value. |
thresholds | Array<{value, color, label?}> | — | — | Threshold zones. Each zone's value is the upper bound. Last value should equal max. |
gradientFill | object | — | — | Arc-length gradient for the gauge band. The gradient is sampled along the visible filled portion of the arc from the sweep start toward the current value. If fillZones is false, the entire arc uses the gradient. |
color | string | — | theme primary | Fill color when no thresholds defined. |
backgroundColor | string | — | #e0e0e0 | Background arc color. |
arcWidth | number | — | 0.3 | Arc thickness as fraction of radius (0–1). |
sweep | number | — | 240 | Arc sweep angle in degrees. |
showNeedle | boolean | — | true | Show a needle indicator at the current value. |
needleColor | string | — | theme text | Needle stroke color. |
centerContent | ReactNode | (value, min, max) => ReactNode | — | value label | Custom content rendered at the gauge center. |
valueFormat | (value) => string | — | Math.round | Format function for the default center value label. |
showScaleLabels | boolean | — | true | Show scale labels at threshold boundaries. |
enableHover | boolean | — | true | Enable hover interaction on arc segments. |
tooltip | TooltipProp | — | default | Tooltip on arc hover. |
annotations | Array<object> | — | — | Annotation objects — supports gauge-label, gauge-needle, and standard types. |
- DonutChart — multi-category ring chart (GaugeChart is built on the same rendering pipeline)
- PieChart — part-to-whole without the inner radius
- Annotations — adding callouts, highlights, and notes to any visualization