import { MeterStack, MeterStackSegment, Text } from '@vega-ui/react';
<div>
<div>
<Text id='preview-meter-stack-label' size={2} fontWeight={500}>Storage</Text>
<Text size={2}>46 of 64 GB</Text>
</div>
<MeterStack value={46} max={64} role='group' aria-labelledby='preview-meter-stack-label' fullWidth>
{STORAGE.map((item) => (
<MeterStackSegment
key={item.label}
value={item.value}- Browser support
- Widely available
- Chrome 111
- Edge 111
- Firefox 113
- Safari 15.4
- Safari on iOS 15.4
- Bundle size
- Oklab and OkLCh — comes from the design tokens
- :where()
Summary
MeterStack is a compound component for segmented meter displays. It shows an aggregate value as multiple colored segments instead of one continuous bar, making composition and distribution the primary message.
Imports
import {
MeterStack,
MeterStackSegment,
type MeterStackProps,
type MeterStackSegmentProps,
type MeterStackSize,
} from '@vega-ui/react';Exported Types
MeterStackPropsMeterStackSegmentPropsMeterStackSize
Minimal Composition
MeterStack itself has no role, while every MeterStackSegment renders role='meter'. Give the root role='group' with an accessible name and name each segment, otherwise a screen reader hears several unnamed meters. Segment widths are value / max, so max must be on the same scale as the segment values.
import { MeterStack, MeterStackSegment } from '@vega-ui/react';
export default function MinimalComposition() {
return (
<MeterStack value={0.8} max={1} role='group' aria-label='Disk usage' fullWidth>
<MeterStackSegment value={0.5} aria-label='Apps' />
<MeterStackSegment value={0.3} aria-label='Photos' />
</MeterStack>
);
}Required Parts
MeterStack: root meter surface and shared range contextMeterStackSegment: one segment of the aggregate
Optional Parts
- custom color overrides on each segment through style variables
Composition Order
MeterStack- repeated
MeterStackSegment
Variants
- Size:
sm,md,lg - Layout: fixed width or
fullWidth - Segment model: any number of ordered segments
Edge Cases
- Segment ordering should reflect real meaning.
- Very small segments may need labels or tooltips outside the component.
- Segment totals are a consumer concern and should be validated in the parent feature.
- If the segments do not encode real meaning, one continuous
Meteris usually less noisy and more honest.
Common Mistakes
- Letting segment totals exceed the intended range.
- Using too many tiny segments with no external labeling.
- Treating segment colors as arbitrary decoration instead of meaningful encoding.
Examples
Basic
Basic: storage breakdown with a legend
One total split into categories on a real scale (max is the device capacity in gigabytes). Each segment gets its color through --meter-stack-segment-color, its own aria-label and a readable aria-valuetext, and the root is a named group. The legend repeats the numbers in text so the split does not depend on color.
import { MeterStack, MeterStackSegment, Text } from '@vega-ui/react';
import { CSSProperties, useId } from 'react';
const categories = [
{ id: 'apps', label: 'Apps', gb: 82, color: 'var(--color-blue-500)' },
{ id: 'photos', label: 'Photos', gb: 64, color: 'var(--color-purple-500)' },
{ id: 'messages', label: 'Messages', gb: 18, color: 'var(--color-green-500)' },
{ id: 'system', label: 'System', gb: 31, color: 'var(--color-gray-500)' },
];
export default function StorageBreakdown() {
const titleId = useId();Controlled/Stateful
Controlled/Stateful: service health by period
The parent owns the selected period and derives the segment values from it. Switching between 24 hours, 7 days and 30 days animates the segment widths, and the uptime percentage above the bar is computed from the same numbers.
import { Button, MeterStack, MeterStackSegment, Text } from '@vega-ui/react';
import { CSSProperties, useId, useState } from 'react';
type Period = '24h' | '7d' | '30d';
const minutesByPeriod: Record<Period, { healthy: number, degraded: number, outage: number }> = {
'24h': { healthy: 1380, degraded: 52, outage: 8 },
'7d': { healthy: 9640, degraded: 410, outage: 30 },
'30d': { healthy: 42310, degraded: 690, outage: 200 },
};
const periods: Period[] = ['24h', '7d', '30d'];Form/Integration
Form/Integration: password strength
A password field in a real form drives four checks; each passed check adds one equal segment, and all segments take the color of the current level. The bar is decorative here (aria-hidden), while the strength text is linked to the input with aria-describedby, so a screen reader hears it on focus. The submit handler reads the value with FormData.
import { Button, HelperText, Label, MeterStack, MeterStackSegment, TextField, TextFieldInput } from '@vega-ui/react';
import { CSSProperties, FormEvent, useId, useState } from 'react';
const levels = [
{ label: 'Too weak', color: 'var(--color-red-500)' },
{ label: 'Weak', color: 'var(--color-red-500)' },
{ label: 'Fair', color: 'var(--color-orange-500)' },
{ label: 'Good', color: 'var(--color-amber-500)' },
{ label: 'Strong', color: 'var(--color-green-500)' },
];
export default function PasswordStrengthBreakdown() {Layout/Overlay
Layout/Overlay: revenue split in a card
A card summary with a large stack and a description list underneath: amount and share per channel. The heading names the group, and currency formatting comes from Intl.NumberFormat, the same values feeding both the bar and the list.
import { Card, Heading, MeterStack, MeterStackSegment, Text } from '@vega-ui/react';
import { CSSProperties, useId } from 'react';
const channels = [
{ id: 'web', label: 'Web store', amount: 184200, color: 'var(--color-blue-500)' },
{ id: 'ios', label: 'iOS app', amount: 121700, color: 'var(--color-purple-500)' },
{ id: 'android', label: 'Android app', amount: 64300, color: 'var(--color-green-500)' },
{ id: 'partners', label: 'Partners', amount: 29800, color: 'var(--color-orange-500)' },
];
export default function RevenueSplitSummary() {
const titleId = useId();Error
Error: seats over the plan limit
When the segments add up to more than max, the stack clips the overflow, so the bar alone looks merely full. The example compares the sum with the limit, recolors the overflowing segment and links an error HelperText to the group with aria-describedby; removing guests clears the error.
import { Button, HelperText, MeterStack, MeterStackSegment, Text } from '@vega-ui/react';
import { CSSProperties, useId, useState } from 'react';
export default function OverloadedQuotaBreakdown() {
const titleId = useId();
const errorId = useId();
const seats = 25;
const admins = 3;
const members = 18;
const [guests, setGuests] = useState(3);
const used = admins + members + guests;Edge
Edge: many tiny segments grouped into “Other”
Segments under 2% of the total are too thin to see or to label. The example merges them into one “Other” segment, keeps the segment count small and lists the merged items in text.
import { MeterStack, MeterStackSegment, Text } from '@vega-ui/react';
import { CSSProperties, useId } from 'react';
const languages = [
{ name: 'TypeScript', bytes: 812400 },
{ name: 'CSS', bytes: 146900 },
{ name: 'MDX', bytes: 61300 },
{ name: 'JavaScript', bytes: 18200 },
{ name: 'Shell', bytes: 4100 },
{ name: 'HTML', bytes: 2600 },
{ name: 'Dockerfile', bytes: 900 },
{ name: 'Makefile', bytes: 400 },Anatomy
Overview
MeterStack provides one shared value range and renders multiple segments inside the same filled surface. Each segment declares its own value relative to the root max.
Required Parts
MeterStack
Required. Owns the aggregate value range, size, and background track.
MeterStackSegment
Required in normal composition. Renders one proportional segment.
Optional Parts
Segment color overrides
Optional. Each segment can override its color through CSS variables.
Composition Order
MeterStack- repeated
MeterStackSegment
Valid Composition Patterns
import { MeterStack, MeterStackSegment } from '@vega-ui/react';
export default function MinimalComposition() {
return (
<MeterStack value={0.8} max={1} role='group' aria-label='Disk usage' fullWidth>
<MeterStackSegment value={0.5} aria-label='Apps' />
<MeterStackSegment value={0.3} aria-label='Photos' />
</MeterStack>
);
}Invalid Composition Patterns
Segment values that do not reflect the intended total
The visual meter stops being trustworthy.
Too many micro-segments without explanation
The component becomes visually noisy and hard to interpret.