Skip to content
v2.5.0
English
Русский
Documentation

MeterStack

MeterStack is a compound component for segmented meter displays.
Storage46 of 64 GB
meter-stack.tsx · 19 lines
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
Tested in
14 tests

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

  • MeterStackProps
  • MeterStackSegmentProps
  • MeterStackSize

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.

minimal-composition.tsx · 10 lines
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 context
  • MeterStackSegment: one segment of the aggregate

Optional Parts

  • custom color overrides on each segment through style variables

Composition Order

  1. MeterStack
  2. 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 Meter is 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.

storage-breakdown.tsx · 46 lines
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.

service-health-by-period.tsx · 69 lines
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.

password-strength-breakdown.tsx · 70 lines
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.

revenue-split-summary.tsx · 51 lines
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.

overloaded-quota-breakdown.tsx · 58 lines
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.

many-small-segments.tsx · 63 lines
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

  1. MeterStack
  2. repeated MeterStackSegment

Valid Composition Patterns

minimal-composition.tsx · 10 lines
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.