Mapcn
blockanalytics-cardsource: ok · hit
Analytics Card
Compact analytics card with a headline metric over a world map.
npx shadcn add "https://mapcn.dev/r/analytics-card.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 · lighticonslucide-react
Registry dependencies
@mapcn/mapcardbadge
Files2 · source cached 10/8/2026, 9:05:24 PM
src/registry/blocks/analytics-card/page.tsx
"use client";
import { TrendingUp } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import {
Card,
CardAction,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Map, MapGeoJSON, MapMarker, MarkerContent } from "@/components/ui/map";
import { totalVisitors, visitorGrowth, visitorLocations } from "./data";
// Country borders from a public CDN, or swap in your own GeoJSON.
const WORLD_GEOJSON =
"https://cdn.jsdelivr.net/gh/nvkelso/natural-earth-vector@v5.1.2/geojson/ne_110m_admin_0_countries.geojson";
function bubbleSize(visitors: number) {
return Math.round(10 + Math.sqrt(visitors) * 2.2);
}
export default function Page() {
return (
<div className="flex min-h-screen items-center justify-center p-8">
<Card className="relative aspect-16/10 w-full max-w-md overflow-hidden py-0">
<div className="absolute inset-0">
<Map
center={[1, 30]}
scrollZoom={false}
dragRotate={false}
dragPan={false}
doubleClickZoom={false}
pitchWithRotate={false}
className="[&_.maplibregl-canvas]:cursor-default! [&_.maplibregl-canvas-container]:cursor-default!"
blank
>
<MapGeoJSON data={WORLD_GEOJSON} linePaint={false} />
{visitorLocations.map((location) => {
const size = bubbleSize(location.visitors);
return (
<MapMarker
key={location.city}
longitude={location.lng}
latitude={location.lat}
>
<MarkerContent className="cursor-default">
<span
className="bg-chart-2/80 block rounded-full"
style={{ width: size, height: size }}
/>
</MarkerContent>
</MapMarker>
);
})}
</Map>
</div>
<CardHeader className="from-card via-card/85 to-card/0 relative z-10 gap-1 rounded-t-[inherit] bg-linear-to-b pt-4 pb-10 mask-[linear-gradient(to_bottom,black_calc(100%_-_2.5rem),transparent)] backdrop-blur-[2px]">
<CardDescription>Visitors</CardDescription>
<CardTitle className="text-lg tabular-nums">
{totalVisitors}
</CardTitle>
<CardAction>
<Badge variant="outline">
<TrendingUp />
{visitorGrowth} growth
</Badge>
</CardAction>
</CardHeader>
</Card>
</div>
);
}
src/registry/blocks/analytics-card/data.ts
export interface VisitorLocation {
city: string;
lng: number;
lat: number;
visitors: number;
}
export const totalVisitors = "418.2K";
export const visitorGrowth = "+12%";
export const visitorLocations: VisitorLocation[] = [
{ city: "San Francisco", lng: -122.4194, lat: 37.7749, visitors: 62 },
{ city: "New York", lng: -74.006, lat: 40.7128, visitors: 58 },
{ city: "Mexico City", lng: -99.1332, lat: 19.4326, visitors: 22 },
{ city: "Sao Paulo", lng: -46.6333, lat: -23.5505, visitors: 30 },
{ city: "London", lng: -0.1276, lat: 51.5074, visitors: 48 },
{ city: "Berlin", lng: 13.405, lat: 52.52, visitors: 28 },
{ city: "Madrid", lng: -3.7038, lat: 40.4168, visitors: 18 },
{ city: "Lagos", lng: 3.3792, lat: 6.5244, visitors: 16 },
{ city: "Dubai", lng: 55.2708, lat: 25.2048, visitors: 24 },
{ city: "Mumbai", lng: 72.8777, lat: 19.076, visitors: 40 },
{ city: "Singapore", lng: 103.8198, lat: 1.3521, visitors: 21 },
{ city: "Tokyo", lng: 139.6917, lat: 35.6895, visitors: 38 },
{ city: "Seoul", lng: 126.978, lat: 37.5665, visitors: 26 },
{ city: "Sydney", lng: 151.2093, lat: -33.8688, visitors: 17 },
];