Skip to content
Magic UI SolidMagicUI Solid

Dotted Map

A component with a svg dotted map.

SeoulNYC
@/demos/DottedMapDemo.tsx
import type { TCountryCode } from 'countries-list'
import type { Marker } from '@/registry/magicui/dotted-map'
import { DottedMap } from '@/registry/magicui/dotted-map'
type CountryCode = Lowercase<TCountryCode>
type MyMarker = Marker & {
overlay: {
countryCode: CountryCode
label: string
}
}
const markers: MyMarker[] = [
{
lat: 37.5665,
lng: 126.978,
size: 2.8,
overlay: { countryCode: 'kr', label: 'Seoul' },
},
{
lat: 40.7128,
lng: -74.006,
size: 2.8,
overlay: { countryCode: 'us', label: 'NYC' },
},
]
export function DottedMapDemo() {
return (
<div class="relative h-125 w-full overflow-hidden rounded-lg border">
<div class="
absolute inset-0 bg-radial from-transparent to-background to-200%
"
/>
<DottedMap<MyMarker>
markers={markers}
renderMarkerOverlay={({ marker, x, y, r }) => {
const { countryCode, label } = marker.overlay
const href = `https://flagcdn.com/w80/${countryCode}.webp`
const imgR = r * 0.75
const fontSize = r * 0.9
const pillH = r * 1.5
const pillW = label.length * (fontSize * 0.62) + r * 1.4
const pillX = x + r + r * 0.6
const pillY = y - pillH / 2
return (
<g style={{ 'pointer-events': 'none' }}>
<image
href={href}
x={x - imgR}
y={y - imgR}
width={imgR * 2}
height={imgR * 2}
preserveAspectRatio="xMidYMid slice"
style={{ 'clip-path': 'circle(50% at 50% 50%)' }}
/>
<rect
x={pillX}
y={pillY}
width={pillW}
height={pillH}
rx={pillH / 2}
fill="rgba(0,0,0,0.55)"
/>
<text
x={pillX + r * 0.7}
y={y + fontSize * 0.35}
style={{ 'font-size': `${fontSize}px` }}
fill="white"
>
{label}
</text>
</g>
)
}}
/>
</div>
)
}
Terminal window
pnpm dlx shadcn@latest add @magicui-solid/dotted-map
@/demos/DottedMapDemo2.tsx
import { DottedMap } from '@/registry/magicui/dotted-map'
export function DottedMapDemo2() {
return (
<div class="relative h-125 w-full overflow-hidden rounded-lg border">
<DottedMap dotRadius={0.1} />
</div>
)
}
@/demos/DottedMapDemo3.tsx
import type { Marker } from '@/registry/magicui/dotted-map'
import { DottedMap } from '@/registry/magicui/dotted-map'
const markers: Marker[] = [
{
lat: 37.5665,
lng: 126.978,
size: 0.3,
},
{
lat: 40.7128,
lng: -74.006,
size: 0.3,
pulse: false,
},
]
export function DottedMapDemo3() {
return (
<div class="relative h-125 w-full overflow-hidden rounded-lg border">
<div class="
absolute inset-0 bg-radial from-transparent to-background to-200%
"
/>
<DottedMap markers={markers} pulse />
</div>
)
}
import { DottedMap } from '@/components/magicui/dotted-map'
<div class="relative h-[400px] w-full overflow-hidden rounded-xl border">
<DottedMap />
</div>
import { DottedMap, type Marker } from '@/components/magicui/dotted-map'
import type { TCountryCode } from 'countries-list'
type CountryCode = Lowercase<TCountryCode>
type MyMarker = Marker & {
overlay: {
countryCode: CountryCode
label: string
}
}
const markers: MyMarker[] = [
{
lat: 37.5665,
lng: 126.978,
size: 2.8,
overlay: { countryCode: 'kr', label: 'Seoul' },
},
]
<DottedMap<MyMarker>
markers={markers}
renderMarkerOverlay={({ marker, x, y, r }) => {
// Custom overlay rendering
return <text x={x} y={y}>{marker.overlay.label}</text>
}}
/>
Prop Type Default Description
width number 150 Width of the SVG map.
height number 75 Height of the SVG map.
mapSamples number 5000 Number of sample points for map generation.
markers Marker[] [] Array of markers to display on the map.
dotColor string currentColor Color of the map dots.
markerColor string "#FF6900" Color of the markers.
dotRadius number 0.2 Radius of the map dots.
stagger boolean true Whether to stagger dots in alternating rows for visual effect.
pulse boolean false Enables built-in pulse animation for markers.
renderMarkerOverlay (args) => JSX.Element undefined Custom render function for marker overlays.
className string undefined Additional class names applied to the SVG.
style CSSProperties undefined Inline styles merged into the SVG.
interface Marker {
lat: number
lng: number
size?: number
pulse?: boolean
}

Set pulse on DottedMap to animate all markers (and use marker.pulse = false to opt out per marker). Without the pulse prop, only markers with marker.pulse = true will pulse.

The DottedMap component supports generic types, allowing you to extend the Marker interface with custom properties:

import type { TCountryCode } from 'countries-list'
type CountryCode = Lowercase<TCountryCode>
type MyMarker = Marker & {
overlay: {
countryCode: CountryCode
label: string
}
}
<DottedMap<MyMarker> markers={markers} />

Original:

magicui.design/docs/components/dotted-map