shadimage
Mapcn
blockuptime-monitorsource: ok · hit

Uptime Monitor

Status-page style map of edge network locations with live health, latency, and uptime.

npx shadcn add "https://mapcn.dev/r/uptime-monitor.json"
free

Where it goes

stats

Interaction

animated-counter

Tags auto-generated by rules on 9/24/2026 — empty means no evidence, not lack of support.

Dependencies

Bundle weight · light

Registry dependencies

@mapcn/map

Files4 · source cached 10/8/2026, 10:25:08 PM

src/registry/blocks/uptime-monitor/page.tsx
"use client";

import { Map, MapControls, MapGeoJSON } from "@/components/ui/map";
import { EdgeNodeMarker } from "./components/edge-node-marker";
import { StatusSidebar } from "./components/status-sidebar";
import { edgeNodes, mapView, WORLD_GEOJSON } from "./data";

export default function Page() {
  return (
    <div className="flex min-h-screen items-center justify-center p-4">
      <div className="bg-card flex h-[500px] w-full max-w-4xl overflow-hidden rounded-xl border shadow-sm">
        <StatusSidebar nodes={edgeNodes} />

        <div className="relative min-w-0 flex-1">
          <Map
            blank
            center={mapView.center}
            zoom={mapView.zoom}
            minZoom={mapView.minZoom}
            maxZoom={mapView.maxZoom}
            scrollZoom={false}
            dragRotate={false}
            pitchWithRotate={false}
          >
            <MapGeoJSON data={WORLD_GEOJSON} linePaint={false} />

            {edgeNodes.map((node) => (
              <EdgeNodeMarker key={node.id} node={node} />
            ))}

            <MapControls className="bottom-2" />
          </Map>
        </div>
      </div>
    </div>
  );
}
src/registry/blocks/uptime-monitor/data.ts
export type EdgeStatus = "operational" | "degraded" | "down";

export interface EdgeNode {
  id: string;
  city: string;
  region: string;
  lng: number;
  lat: number;
  status: EdgeStatus;
  latency: number;
  uptime: number;
}

export const statusMeta: Record<EdgeStatus, { dot: string; text: string }> = {
  operational: {
    dot: "bg-emerald-500",
    text: "text-emerald-600 dark:text-emerald-400",
  },
  degraded: {
    dot: "bg-amber-500",
    text: "text-amber-600 dark:text-amber-400",
  },
  down: {
    dot: "bg-red-500",
    text: "text-red-600 dark:text-red-400",
  },
};

/** Country borders served from a public CDN — swap in your own GeoJSON. */
export const WORLD_GEOJSON =
  "https://cdn.jsdelivr.net/gh/nvkelso/natural-earth-vector@v5.1.2/geojson/ne_110m_admin_0_countries.geojson";

export const mapView = {
  center: [5, 28] as [number, number],
  zoom: 0.45,
  minZoom: 0.45,
  maxZoom: 2,
};

/** Edge network points of presence. */
export const edgeNodes: EdgeNode[] = [
  {
    id: "iad",
    city: "Washington, D.C.",
    region: "North America",
    lng: -77.0369,
    lat: 38.9072,
    status: "operational",
    latency: 24,
    uptime: 99.99,
  },
  {
    id: "sfo",
    city: "San Francisco",
    region: "North America",
    lng: -122.4194,
    lat: 37.7749,
    status: "operational",
    latency: 31,
    uptime: 99.98,
  },
  {
    id: "yyz",
    city: "Toronto",
    region: "North America",
    lng: -79.3832,
    lat: 43.6532,
    status: "degraded",
    latency: 88,
    uptime: 99.21,
  },
  {
    id: "gru",
    city: "São Paulo",
    region: "South America",
    lng: -46.6333,
    lat: -23.5505,
    status: "operational",
    latency: 47,
    uptime: 99.95,
  },
  {
    id: "lhr",
    city: "London",
    region: "Europe",
    lng: -0.1276,
    lat: 51.5074,
    status: "operational",
    latency: 19,
    uptime: 99.99,
  },
  {
    id: "fra",
    city: "Frankfurt",
    region: "Europe",
    lng: 8.6821,
    lat: 50.1109,
    status: "operational",
    latency: 22,
    uptime: 99.97,
  },
  {
    id: "cdg",
    city: "Paris",
    region: "Europe",
    lng: 2.3522,
    lat: 48.8566,
    status: "down",
    latency: 0,
    uptime: 97.84,
  },
  {
    id: "jnb",
    city: "Johannesburg",
    region: "Africa",
    lng: 28.0473,
    lat: -26.2041,
    status: "operational",
    latency: 64,
    uptime: 99.92,
  },
  {
    id: "bom",
    city: "Mumbai",
    region: "Asia Pacific",
    lng: 72.8777,
    lat: 19.076,
    status: "operational",
    latency: 41,
    uptime: 99.96,
  },
  {
    id: "sin",
    city: "Singapore",
    region: "Asia Pacific",
    lng: 103.8198,
    lat: 1.3521,
    status: "degraded",
    latency: 73,
    uptime: 99.45,
  },
  {
    id: "nrt",
    city: "Tokyo",
    region: "Asia Pacific",
    lng: 139.6917,
    lat: 35.6895,
    status: "operational",
    latency: 28,
    uptime: 99.98,
  },
  {
    id: "syd",
    city: "Sydney",
    region: "Asia Pacific",
    lng: 151.2093,
    lat: -33.8688,
    status: "operational",
    latency: 52,
    uptime: 99.94,
  },
  {
    id: "lax",
    city: "Los Angeles",
    region: "North America",
    lng: -118.2437,
    lat: 34.0522,
    status: "operational",
    latency: 33,
    uptime: 99.97,
  },
  {
    id: "ord",
    city: "Chicago",
    region: "North America",
    lng: -87.6298,
    lat: 41.8781,
    status: "operational",
    latency: 27,
    uptime: 99.98,
  },
  {
    id: "scl",
    city: "Santiago",
    region: "South America",
    lng: -70.6693,
    lat: -33.4489,
    status: "operational",
    latency: 58,
    uptime: 99.91,
  },
  {
    id: "ams",
    city: "Amsterdam",
    region: "Europe",
    lng: 4.9041,
    lat: 52.3676,
    status: "operational",
    latency: 21,
    uptime: 99.98,
  },
  {
    id: "mad",
    city: "Madrid",
    region: "Europe",
    lng: -3.7038,
    lat: 40.4168,
    status: "degraded",
    latency: 79,
    uptime: 99.32,
  },
  {
    id: "arn",
    city: "Stockholm",
    region: "Europe",
    lng: 18.0686,
    lat: 59.3293,
    status: "operational",
    latency: 26,
    uptime: 99.96,
  },
  {
    id: "dxb",
    city: "Dubai",
    region: "Middle East",
    lng: 55.2708,
    lat: 25.2048,
    status: "operational",
    latency: 44,
    uptime: 99.95,
  },
  {
    id: "hkg",
    city: "Hong Kong",
    region: "Asia Pacific",
    lng: 114.1694,
    lat: 22.3193,
    status: "operational",
    latency: 36,
    uptime: 99.97,
  },
  {
    id: "icn",
    city: "Seoul",
    region: "Asia Pacific",
    lng: 126.978,
    lat: 37.5665,
    status: "operational",
    latency: 30,
    uptime: 99.98,
  },
  {
    id: "los",
    city: "Lagos",
    region: "Africa",
    lng: 3.3792,
    lat: 6.5244,
    status: "down",
    latency: 0,
    uptime: 98.12,
  },
];

export interface NetworkSummary {
  status: EdgeStatus;
  label: string;
  operational: number;
  total: number;
  avgUptime: number;
}

/** Derive the overall network health from the individual edge nodes. */
export function getNetworkSummary(nodes: EdgeNode[]): NetworkSummary {
  const total = nodes.length;
  const operational = nodes.filter((n) => n.status === "operational").length;
  const hasDown = nodes.some((n) => n.status === "down");
  const hasDegraded = nodes.some((n) => n.status === "degraded");

  const status: EdgeStatus = hasDown
    ? "down"
    : hasDegraded
      ? "degraded"
      : "operational";

  const label = hasDown
    ? "Partial outage"
    : hasDegraded
      ? "Degraded performance"
      : "All systems operational";

  const avgUptime =
    nodes.reduce((sum, n) => sum + n.uptime, 0) / Math.max(total, 1);

  return { status, label, operational, total, avgUptime };
}
src/registry/blocks/uptime-monitor/components/edge-node-marker.tsx
"use client";

import { cn } from "@/lib/utils";
import { MapMarker, MarkerContent, MarkerTooltip } from "@/components/ui/map";
import { statusMeta, type EdgeNode } from "../data";

interface EdgeNodeMarkerProps {
  node: EdgeNode;
}

export function EdgeNodeMarker({ node }: EdgeNodeMarkerProps) {
  const meta = statusMeta[node.status];

  return (
    <MapMarker longitude={node.lng} latitude={node.lat}>
      <MarkerContent>
        <div className="group flex size-4 items-center justify-center">
          <span
            className={cn(
              "inline-flex size-2 rounded-full transition-transform group-hover:scale-125",
              meta.dot,
            )}
          />
        </div>
      </MarkerContent>
      <MarkerTooltip
        offset={10}
        className="bg-popover text-popover-foreground min-w-28 border p-2"
      >
        <div className="flex items-center gap-1.5">
          <span className={cn("size-1.5 rounded-full", meta.dot)} />
          <span className="text-[11px] font-medium">{node.city}</span>
          <span className="text-muted-foreground ml-auto font-mono text-[11px] uppercase">
            {node.id}
          </span>
        </div>
        <div className="text-muted-foreground mt-1.5 flex items-center justify-between gap-3 text-[11px]">
          <span>{node.status === "down" ? "—" : `${node.latency} ms`}</span>
          <span className="tabular-nums">{node.uptime.toFixed(2)}%</span>
        </div>
      </MarkerTooltip>
    </MapMarker>
  );
}
src/registry/blocks/uptime-monitor/components/status-sidebar.tsx
"use client";

import { cn } from "@/lib/utils";
import { getNetworkSummary, statusMeta, type EdgeNode } from "../data";

interface StatusSidebarProps {
  nodes: EdgeNode[];
}

export function StatusSidebar({ nodes }: StatusSidebarProps) {
  const summary = getNetworkSummary(nodes);
  const summaryMeta = statusMeta[summary.status];

  return (
    <div className="bg-card flex w-56 shrink-0 flex-col overflow-hidden border-r">
      <div className="border-b p-3">
        <p className="text-foreground text-sm font-semibold">Edge Network</p>

        <div className="mt-2 flex items-center gap-1.5">
          <span className={cn("size-2 rounded-full", summaryMeta.dot)} />
          <span className={cn("text-xs font-medium", summaryMeta.text)}>
            {summary.label}
          </span>
        </div>

        <div className="mt-3 grid grid-cols-2 gap-2">
          <div className="bg-background/60 rounded-md border p-2">
            <p className="text-muted-foreground text-[10px] tracking-wide uppercase">
              Uptime
            </p>
            <p className="text-foreground text-sm font-semibold tabular-nums">
              {summary.avgUptime.toFixed(2)}%
            </p>
          </div>
          <div className="bg-background/60 rounded-md border p-2">
            <p className="text-muted-foreground text-[10px] tracking-wide uppercase">
              Edges up
            </p>
            <p className="text-foreground text-sm font-semibold tabular-nums">
              {summary.operational}
              <span className="text-muted-foreground font-normal">
                /{summary.total}
              </span>
            </p>
          </div>
        </div>
      </div>

      <ul className="flex-1 divide-y overflow-y-auto">
        {nodes.map((node) => {
          const meta = statusMeta[node.status];

          return (
            <li key={node.id} className="flex items-center gap-2.5 px-3 py-2">
              <span className={cn("size-2 shrink-0 rounded-full", meta.dot)} />

              <div className="min-w-0 flex-1">
                <p className="text-foreground truncate text-xs font-medium">
                  {node.city}
                </p>
                <p className="text-muted-foreground truncate text-[10px]">
                  {node.region}
                </p>
              </div>

              <div className="flex shrink-0 flex-col items-end">
                <span className="text-muted-foreground font-mono text-[10px] uppercase">
                  {node.id}
                </span>
                <span className="text-foreground font-mono text-[10px] tabular-nums">
                  {node.status === "down" ? "—" : `${node.latency}ms`}
                </span>
              </div>
            </li>
          );
        })}
      </ul>
    </div>
  );
}

More from Mapcn

Browse all components in this library →