Web InnoventixFreeCode

Map Stat

Original · free

stats with a dotted world map

byWeb InnoventixReact + Tailwind
statxmapstats
Open

Copy prompt gives Claude Code, Cursor or v0 a ready-to-paste prompt that recreates this exact component.

npx shadcn@latest add https://webinnoventix.com/r/statx-map.json
statx-map.tsx
"use client";

import { type KeyboardEvent, useMemo, useRef, useState } from "react";
import { motion, useReducedMotion } from "motion/react";

type MetricId = "users" | "requests" | "growth";

type Region = {
  id: string;
  name: string;
  hub: string;
  c: number;
  r: number;
  users: number;
  requestsM: number;
  growth: number;
  latency: number;
};

type Ellipse = { cx: number; cy: number; rx: number; ry: number };

const COLS = 60;
const ROWS = 28;
const STEP = 10;
const PAD = 10;
const VB_W = (COLS - 1) * STEP + PAD * 2;
const VB_H = (ROWS - 1) * STEP + PAD * 2;

const LAND: Ellipse[] = [
  { cx: 10, cy: 5, rx: 7, ry: 3 },
  { cx: 9, cy: 8, rx: 5.5, ry: 3 },
  { cx: 13.5, cy: 11.5, rx: 2.6, ry: 2.2 },
  { cx: 24, cy: 2.5, rx: 2.2, ry: 2 },
  { cx: 19, cy: 17.5, rx: 3.4, ry: 3 },
  { cx: 18.5, cy: 22, rx: 2.3, ry: 4 },
  { cx: 30, cy: 6, rx: 3, ry: 2.4 },
  { cx: 31.5, cy: 12.5, rx: 3.6, ry: 2.6 },
  { cx: 32.5, cy: 17.5, rx: 3, ry: 3 },
  { cx: 44, cy: 6, rx: 10, ry: 4 },
  { cx: 39.5, cy: 9.5, rx: 3, ry: 2 },
  { cx: 46.5, cy: 11.5, rx: 3.6, ry: 2.4 },
  { cx: 51.5, cy: 9, rx: 3.2, ry: 2.6 },
  { cx: 49.5, cy: 14, rx: 2.4, ry: 1.6 },
  { cx: 52.5, cy: 21, rx: 3.4, ry: 2.4 },
];

function inLand(c: number, r: number): boolean {
  for (const e of LAND) {
    const dx = (c - e.cx) / e.rx;
    const dy = (r - e.cy) / e.ry;
    if (dx * dx + dy * dy <= 1) return true;
  }
  return false;
}

const DOTS: { x: number; y: number }[] = [];
for (let r = 0; r < ROWS; r++) {
  for (let c = 0; c < COLS; c++) {
    if (inLand(c, r)) DOTS.push({ x: c * STEP, y: r * STEP });
  }
}

const REGIONS: Region[] = [
  { id: "na", name: "North America", hub: "Ashburn · Toronto", c: 10, r: 8, users: 942000, requestsM: 3100, growth: 14.2, latency: 28 },
  { id: "eu", name: "Europe", hub: "Frankfurt · Dublin", c: 30, r: 6, users: 655000, requestsM: 2400, growth: 11.4, latency: 22 },
  { id: "apac", name: "Asia Pacific", hub: "Singapore · Tokyo", c: 47, r: 10, users: 806000, requestsM: 2600, growth: 29.7, latency: 39 },
  { id: "latam", name: "Latin America", hub: "São Paulo · Bogotá", c: 19, r: 19, users: 318000, requestsM: 690, growth: 38.6, latency: 46 },
  { id: "mea", name: "Middle East & Africa", hub: "Nairobi · Dubai", c: 32, r: 15, users: 174000, requestsM: 410, growth: 52.3, latency: 61 },
  { id: "oce", name: "Oceania", hub: "Sydney · Auckland", c: 52, r: 21, users: 96000, requestsM: 240, growth: 19.8, latency: 44 },
];

const METRICS: { id: MetricId; label: string }[] = [
  { id: "users", label: "Active devs" },
  { id: "requests", label: "Requests" },
  { id: "growth", label: "Growth" },
];

function formatUsers(n: number): string {
  if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(2)}M`;
  if (n >= 1_000) return `${Math.round(n / 1_000)}K`;
  return `${n}`;
}

function formatReq(m: number): string {
  if (m >= 1000) return `${(m / 1000).toFixed(1).replace(/\.0$/, "")}B`;
  return `${m}M`;
}

function metricValue(rg: Region, m: MetricId): number {
  return m === "users" ? rg.users : m === "requests" ? rg.requestsM : rg.growth;
}

function metricLabel(v: number, m: MetricId): string {
  return m === "users" ? formatUsers(v) : m === "requests" ? formatReq(v) : `+${v.toFixed(1)}%`;
}

function nodePct(c: number, r: number): { left: number; top: number } {
  return {
    left: ((c * STEP + PAD) / VB_W) * 100,
    top: ((r * STEP + PAD) / VB_H) * 100,
  };
}

export default function StatxMap() {
  const reduce = useReducedMotion();
  const [selectedId, setSelectedId] = useState<string>("na");
  const [metric, setMetric] = useState<MetricId>("users");
  const [hovered, setHovered] = useState<string | null>(null);

  const optionRefs = useRef<(HTMLButtonElement | null)[]>([]);
  const metricRefs = useRef<(HTMLButtonElement | null)[]>([]);

  const selected = REGIONS.find((rg) => rg.id === selectedId) ?? REGIONS[0];

  const stats = useMemo(() => {
    const totalUsers = REGIONS.reduce((a, rg) => a + rg.users, 0);
    const totalReq = REGIONS.reduce((a, rg) => a + rg.requestsM, 0);
    const weighted = REGIONS.reduce((a, rg) => a + rg.users * rg.growth, 0) / totalUsers;
    const sortedLat = REGIONS.map((rg) => rg.latency).sort((a, b) => a - b);
    const mid = sortedLat.length / 2;
    const median = Math.round((sortedLat[mid - 1] + sortedLat[mid]) / 2);
    return { totalUsers, totalReq, weighted, median };
  }, []);

  const barMax = useMemo(
    () => Math.max(...REGIONS.map((rg) => metricValue(rg, metric))),
    [metric],
  );

  const onListKey = (e: KeyboardEvent<HTMLDivElement>) => {
    const idx = REGIONS.findIndex((rg) => rg.id === selectedId);
    let next = idx;
    switch (e.key) {
      case "ArrowRight":
      case "ArrowDown":
        next = (idx + 1) % REGIONS.length;
        break;
      case "ArrowLeft":
      case "ArrowUp":
        next = (idx - 1 + REGIONS.length) % REGIONS.length;
        break;
      case "Home":
        next = 0;
        break;
      case "End":
        next = REGIONS.length - 1;
        break;
      default:
        return;
    }
    e.preventDefault();
    optionRefs.current[next]?.focus();
  };

  const onMetricKey = (e: KeyboardEvent<HTMLDivElement>) => {
    const idx = METRICS.findIndex((m) => m.id === metric);
    let next = idx;
    switch (e.key) {
      case "ArrowRight":
      case "ArrowDown":
        next = (idx + 1) % METRICS.length;
        break;
      case "ArrowLeft":
      case "ArrowUp":
        next = (idx - 1 + METRICS.length) % METRICS.length;
        break;
      default:
        return;
    }
    e.preventDefault();
    setMetric(METRICS[next].id);
    metricRefs.current[next]?.focus();
  };

  const kpis = [
    { label: "Monthly active developers", value: formatUsers(stats.totalUsers), delta: `+${stats.weighted.toFixed(1)}% QoQ`, up: true },
    { label: "Requests served / day", value: formatReq(stats.totalReq), delta: "38 edge locations", up: false },
    { label: "Regions online", value: `${REGIONS.length} / ${REGIONS.length}`, delta: "99.98% SLA", up: true },
    { label: "Median p95 latency", value: `${stats.median} ms`, delta: "−6 ms MoM", up: true },
  ];

  return (
    <section className="relative w-full overflow-hidden bg-white py-20 text-slate-900 sm:py-28 dark:bg-slate-950 dark:text-slate-100">
      <style>{`
        @keyframes statxmap-pulse {
          0%   { transform: scale(1);   opacity: 0.5; }
          70%  { transform: scale(2.8); opacity: 0; }
          100% { transform: scale(2.8); opacity: 0; }
        }
        @keyframes statxmap-flow {
          to { stroke-dashoffset: -18; }
        }
        @keyframes statxmap-fade {
          from { opacity: 0; transform: translateY(6px); }
          to   { opacity: 1; transform: translateY(0); }
        }
        .statxmap-fade { animation: statxmap-fade 0.45s ease both; }
        @media (prefers-reduced-motion: reduce) {
          .statxmap-pulse-ring,
          .statxmap-flow-line,
          .statxmap-fade { animation: none !important; }
        }
      `}</style>

      <div
        aria-hidden
        className="pointer-events-none absolute inset-0"
        style={{
          background:
            "radial-gradient(60% 50% at 50% -8%, rgba(99,102,241,0.16), transparent 70%)",
        }}
      />

      <div className="relative mx-auto max-w-6xl px-6">
        <header className="max-w-2xl">
          <span className="inline-flex items-center gap-2 rounded-full border border-slate-200 bg-slate-50 px-3 py-1 text-xs font-medium tracking-wide text-slate-600 dark:border-slate-800 dark:bg-slate-900 dark:text-slate-400">
            <span className="relative flex h-2 w-2">
              <span
                className="absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-70 statxmap-pulse-ring"
                style={{ animation: reduce ? "none" : "statxmap-pulse 2.4s ease-out infinite", transformOrigin: "center" }}
              />
              <span className="relative inline-flex h-2 w-2 rounded-full bg-emerald-500" />
            </span>
            Global edge network · live
          </span>
          <h2 className="mt-5 text-3xl font-semibold tracking-tight sm:text-4xl">
            Ship once. Serve every region in under 60&nbsp;ms.
          </h2>
          <p className="mt-4 text-base leading-relaxed text-slate-600 dark:text-slate-400">
            Real-time telemetry from the Meridian API platform — 2.99M active developers
            routed through 38 points of presence across six regions. Select a region to
            inspect its traffic.
          </p>
        </header>

        <dl className="mt-10 grid grid-cols-2 gap-3 lg:grid-cols-4">
          {kpis.map((k) => (
            <div
              key={k.label}
              className="rounded-2xl border border-slate-200 bg-slate-50/60 p-4 dark:border-slate-800 dark:bg-slate-900/50"
            >
              <dt className="text-xs font-medium text-slate-500 dark:text-slate-400">{k.label}</dt>
              <dd className="mt-2 text-2xl font-semibold tracking-tight tabular-nums sm:text-3xl">
                {k.value}
              </dd>
              <dd
                className={`mt-1 text-xs font-medium ${
                  k.up ? "text-emerald-600 dark:text-emerald-400" : "text-slate-500 dark:text-slate-400"
                }`}
              >
                {k.delta}
              </dd>
            </div>
          ))}
        </dl>

        <div className="mt-6 grid gap-6 lg:grid-cols-5">
          {/* Map */}
          <div className="lg:col-span-3">
            <div className="relative overflow-hidden rounded-3xl border border-slate-200 bg-gradient-to-b from-slate-50 to-white p-5 dark:border-slate-800 dark:from-slate-900/60 dark:to-slate-950">
              <div className="relative">
                <svg
                  aria-hidden
                  viewBox={`${-PAD} ${-PAD} ${VB_W} ${VB_H}`}
                  className="block h-auto w-full"
                >
                  {DOTS.map((d, i) => (
                    <circle
                      key={i}
                      cx={d.x}
                      cy={d.y}
                      r={1.5}
                      className="text-slate-300 dark:text-slate-700"
                      fill="currentColor"
                    />
                  ))}

                  {REGIONS.filter((rg) => rg.id !== selectedId).map((rg) => (
                    <line
                      key={rg.id}
                      x1={selected.c * STEP}
                      y1={selected.r * STEP}
                      x2={rg.c * STEP}
                      y2={rg.r * STEP}
                      className="statxmap-flow-line text-indigo-400 dark:text-indigo-500"
                      stroke="currentColor"
                      strokeWidth={0.9}
                      strokeOpacity={0.5}
                      strokeDasharray="1.5 4"
                      strokeLinecap="round"
                      style={{ animation: reduce ? "none" : "statxmap-flow 1.1s linear infinite" }}
                    />
                  ))}
                </svg>

                <div
                  role="listbox"
                  aria-label="Select a region to inspect"
                  onKeyDown={onListKey}
                  className="absolute inset-0"
                >
                  {REGIONS.map((rg, i) => {
                    const active = rg.id === selectedId;
                    const p = nodePct(rg.c, rg.r);
                    const showChip = active || hovered === rg.id;
                    return (
                      <button
                        key={rg.id}
                        ref={(el) => {
                          optionRefs.current[i] = el;
                        }}
                        role="option"
                        aria-selected={active}
                        tabIndex={active ? 0 : -1}
                        onClick={() => setSelectedId(rg.id)}
                        onFocus={() => setSelectedId(rg.id)}
                        onMouseEnter={() => setHovered(rg.id)}
                        onMouseLeave={() => setHovered(null)}
                        style={{ left: `${p.left}%`, top: `${p.top}%` }}
                        className="group absolute h-6 w-6 -translate-x-1/2 -translate-y-1/2 rounded-full focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:focus-visible:ring-offset-slate-950"
                      >
                        <span className="sr-only">
                          {rg.name}. {formatUsers(rg.users)} active developers,{" "}
                          {formatReq(rg.requestsM)} requests per day, up {rg.growth.toFixed(1)} percent,
                          p95 latency {rg.latency} milliseconds.
                        </span>

                        {active && (
                          <span
                            aria-hidden
                            className="statxmap-pulse-ring absolute inset-0 rounded-full bg-indigo-500/40"
                            style={{
                              animation: reduce ? "none" : "statxmap-pulse 2.2s ease-out infinite",
                              transformOrigin: "center",
                            }}
                          />
                        )}

                        <span
                          aria-hidden
                          className={`absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 rounded-full ring-2 transition-all ${
                            active
                              ? "h-3.5 w-3.5 bg-indigo-500 ring-white dark:ring-slate-950"
                              : "h-2.5 w-2.5 bg-slate-400 ring-transparent group-hover:bg-indigo-400 dark:bg-slate-500"
                          }`}
                        />

                        <span
                          aria-hidden
                          className={`pointer-events-none absolute bottom-full left-1/2 z-10 mb-2 -translate-x-1/2 whitespace-nowrap rounded-md border px-2 py-1 text-[11px] font-medium shadow-sm transition-opacity ${
                            showChip ? "opacity-100" : "opacity-0"
                          } border-slate-200 bg-white text-slate-700 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-200`}
                        >
                          {rg.name} · {formatUsers(rg.users)}
                        </span>
                      </button>
                    );
                  })}
                </div>
              </div>

              <div className="mt-4 flex items-center justify-between border-t border-slate-200 pt-4 text-xs text-slate-500 dark:border-slate-800 dark:text-slate-400">
                <span className="inline-flex items-center gap-1.5">
                  <svg viewBox="0 0 16 16" className="h-3.5 w-3.5" fill="none" aria-hidden>
                    <circle cx="8" cy="8" r="6.5" stroke="currentColor" strokeWidth="1.4" />
                    <path d="M8 4.5v3.5l2.2 1.3" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round" />
                  </svg>
                  Updated 2 min ago
                </span>
                <span className="tabular-nums">Use arrow keys to move between regions</span>
              </div>
            </div>
          </div>

          {/* Detail panel */}
          <div className="lg:col-span-2">
            <div className="flex h-full flex-col rounded-3xl border border-slate-200 bg-slate-50/60 p-6 dark:border-slate-800 dark:bg-slate-900/50">
              <div key={selected.id} className="statxmap-fade">
                <div className="flex items-baseline justify-between gap-3">
                  <h3 className="text-lg font-semibold tracking-tight">{selected.name}</h3>
                  <span className="rounded-full bg-emerald-100 px-2 py-0.5 text-[11px] font-semibold text-emerald-700 dark:bg-emerald-500/15 dark:text-emerald-400">
                    +{selected.growth.toFixed(1)}%
                  </span>
                </div>
                <p className="mt-0.5 text-xs text-slate-500 dark:text-slate-400">
                  Primary hubs · {selected.hub}
                </p>

                <div className="mt-5 grid grid-cols-2 gap-3">
                  <PanelStat label="Active developers" value={formatUsers(selected.users)} />
                  <PanelStat label="Requests / day" value={formatReq(selected.requestsM)} />
                  <PanelStat label="QoQ growth" value={`+${selected.growth.toFixed(1)}%`} accent />
                  <PanelStat label="p95 latency" value={`${selected.latency} ms`} />
                </div>
              </div>

              <div className="mt-6 border-t border-slate-200 pt-5 dark:border-slate-800">
                <div className="flex items-center justify-between">
                  <p className="text-xs font-medium text-slate-500 dark:text-slate-400">
                    Compare regions by
                  </p>
                  <div
                    role="radiogroup"
                    aria-label="Comparison metric"
                    onKeyDown={onMetricKey}
                    className="inline-flex rounded-lg border border-slate-200 bg-white p-0.5 dark:border-slate-700 dark:bg-slate-950"
                  >
                    {METRICS.map((m, i) => {
                      const on = m.id === metric;
                      return (
                        <button
                          key={m.id}
                          ref={(el) => {
                            metricRefs.current[i] = el;
                          }}
                          role="radio"
                          aria-checked={on}
                          tabIndex={on ? 0 : -1}
                          onClick={() => setMetric(m.id)}
                          className={`rounded-md px-2.5 py-1 text-[11px] font-semibold transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 ${
                            on
                              ? "bg-indigo-600 text-white"
                              : "text-slate-500 hover:text-slate-800 dark:text-slate-400 dark:hover:text-slate-200"
                          }`}
                        >
                          {m.label}
                        </button>
                      );
                    })}
                  </div>
                </div>

                <ul className="mt-4 space-y-2.5">
                  {REGIONS.map((rg) => {
                    const v = metricValue(rg, metric);
                    const on = rg.id === selectedId;
                    return (
                      <li key={rg.id}>
                        <button
                          onClick={() => setSelectedId(rg.id)}
                          className="group grid w-full grid-cols-[7.5rem_1fr_auto] items-center gap-3 rounded-lg px-1 py-1 text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500"
                        >
                          <span
                            className={`truncate text-xs font-medium ${
                              on ? "text-slate-900 dark:text-slate-100" : "text-slate-500 dark:text-slate-400"
                            }`}
                          >
                            {rg.name}
                          </span>
                          <span className="h-2 overflow-hidden rounded-full bg-slate-200 dark:bg-slate-800">
                            <motion.span
                              className={`block h-full rounded-full ${
                                on ? "bg-indigo-500" : "bg-slate-400 group-hover:bg-indigo-400 dark:bg-slate-600"
                              }`}
                              initial={false}
                              animate={{ width: `${(v / barMax) * 100}%` }}
                              transition={reduce ? { duration: 0 } : { type: "spring", stiffness: 90, damping: 18 }}
                            />
                          </span>
                          <span
                            className={`w-14 text-right text-xs font-semibold tabular-nums ${
                              on ? "text-slate-900 dark:text-slate-100" : "text-slate-500 dark:text-slate-400"
                            }`}
                          >
                            {metricLabel(v, metric)}
                          </span>
                        </button>
                      </li>
                    );
                  })}
                </ul>
              </div>
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}

function PanelStat({ label, value, accent }: { label: string; value: string; accent?: boolean }) {
  return (
    <div className="rounded-xl border border-slate-200 bg-white p-3 dark:border-slate-800 dark:bg-slate-950">
      <p className="text-[11px] font-medium text-slate-500 dark:text-slate-400">{label}</p>
      <p
        className={`mt-1 text-lg font-semibold tracking-tight tabular-nums ${
          accent ? "text-emerald-600 dark:text-emerald-400" : ""
        }`}
      >
        {value}
      </p>
    </div>
  );
}

Dependencies

motion

Licence

Built by Web Innoventix. Free for personal and commercial use, no attribution required.

Built by Web Innoventix

Need a custom interface, a full website, or SEO that gets you cited by AI? We design, build and rank it end to end.

Get a free quote

Similar components

Browse all →
Four Stat Metric Band

Four Stat Metric Band

Original

A four-column metric band with gradient figures inside a hairline-divided card, ideal for headline numbers like uptime, latency and growth.

Labelled Stats With Dividers

Labelled Stats With Dividers

Original

Three centred stats separated by responsive dividers, each pairing a headline figure with an uppercase label and supporting detail.

Trusted By Logo Cloud

Trusted By Logo Cloud

Original

A responsive six-up logo strip using simple inline-SVG marks and wordmarks with a grayscale-to-colour hover, for a trusted-by section.

Hero Stat With Supporting Copy

Hero Stat With Supporting Copy

Original

A two-column layout pairing one oversized hero statistic with a supporting paragraph and a checklist card explaining the number.

Count-up stats reveal

Count-up stats reveal

Original

A four-card stat band whose gradient numbers count up from zero as the section scrolls into view, with staggered blur-in cards, a pulsing eyebrow, floating aurora blobs and a shimmering underline sweep.

Animated SVG progress rings

Animated SVG progress rings

Original

Four circular SVG progress rings that draw their gradient arcs and count up to their target percentage the moment they enter view, over a slowly rotating conic halo with a soft glow pulse behind each ring.

Staggered stat band with marquee ticker

Staggered stat band with marquee ticker

Original

A dark stat band where 3D-tilt cards stagger into view with count-up figures over an animated gradient mesh, finished by an infinite marquee ticker of highlight chips that pauses on hover.

Counter Row Stat

Counter Row Stat

Original

animated counter stats row

Cards Stat

Cards Stat

Original

stat cards with icons

Gradient Stat

Gradient Stat

Original

gradient stats band

Split Stat

Split Stat

Original

stats beside copy

Icons Stat

Icons Stat

Original

icon stat grid