Usage / Quota Meter

A stacked quota panel showing current vs. limit for multiple resources. Progress bars shift from green to amber to red as usage approaches the limit, with an optional upgrade prompt at near-limit thresholds.

Dashboard StatsInspired by Vercel
Live PreviewInteractive
Vercel · Usage & Billing
Usage
Bandwidth45%
4.5 / 10 GB
Builds76%
380 / 500 builds
Storage92%
9.2 / 10 GB

Green ≤ 60% · Amber ≤ 85% · Red over 85% · Upgrade hint at near-limit

UsageQuotaMeter.tsx
// Dependencies: react ^18
import React from 'react';

function cn(...classes: (string | false | null | undefined)[]) {
  return classes.filter(Boolean).join(' ');
}

type Meter = {
  id: string;
  label: string;
  used: number;
  total: number;
  unit: string;
};

type Props = {
  meters: Meter[];
  onUpgrade?: () => void;
};

function getMeterColor(pct: number): { bar: string; text: string } {
  if (pct < 0.6) return { bar: '#10b981', text: 'text-emerald-400' };
  if (pct < 0.85) return { bar: '#f59e0b', text: 'text-amber-400' };
  return { bar: '#ef4444', text: 'text-red-400' };
}

export function UsageQuotaMeter({ meters, onUpgrade }: Props) {
  return (
    <div className="w-full flex flex-col bg-[#0a0a0a] border border-[#1a1a1a] rounded-xl overflow-hidden">
      <div className="px-4 py-3 border-b border-[#1a1a1a]">
        <span className="text-sm font-semibold text-white">Usage</span>
      </div>
      <div className="divide-y divide-[#1a1a1a]">
        {meters.map((meter) => {
          const pct = Math.min(meter.used / meter.total, 1);
          const { bar, text } = getMeterColor(pct);
          const nearLimit = pct >= 0.85;
          const pctRounded = Math.round(pct * 100);

          return (
            <div key={meter.id} className="px-4 py-4">
              <div className="flex items-center justify-between mb-2">
                <span className="text-sm text-zinc-300 font-medium">{meter.label}</span>
                <span className={cn('text-xs font-semibold tabular-nums', text)}>
                  {pctRounded}%
                </span>
              </div>
              <div className="relative h-1.5 rounded-full bg-[#1e1e1e] overflow-hidden">
                <div
                  className="absolute inset-y-0 left-0 rounded-full transition-all duration-500"
                  style={{ width: `${pctRounded}%`, backgroundColor: bar }}
                />
              </div>
              <div className="flex items-center justify-between mt-2">
                <span className="text-xs text-zinc-600 tabular-nums">
                  {meter.used.toLocaleString()} / {meter.total.toLocaleString()} {meter.unit}
                </span>
                {nearLimit && onUpgrade && (
                  <button
                    onClick={onUpgrade}
                    className="text-xs text-indigo-400 hover:text-indigo-300 font-medium transition-colors"
                  >
                    Upgrade →
                  </button>
                )}
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

// Usage:
// const METERS = [
//   { id: 'bandwidth', label: 'Bandwidth', used: 4.5, total: 10, unit: 'GB' },
//   { id: 'builds', label: 'Builds', used: 380, total: 500, unit: 'builds' },
//   { id: 'storage', label: 'Storage', used: 9.2, total: 10, unit: 'GB' },
// ];
// <UsageQuotaMeter meters={METERS} onUpgrade={() => router.push('/upgrade')} />