← All primitives
NeonMap
MapA real interactive vector map (pan/zoom/tilt) recolored into a neon nightscape, with a glowing arc and a pulsing pin. Tiles + style from OpenFreeMap — genuinely free, no API token or signup.
Requires the .neon-pin / neon-pulse styles from globals.css and the maplibre-gl CSS (imported in the component). Give it a sized container; render via next/dynamic ssr:false. Separate WebGL context from R3F — don't stack with heavy 3D scenes.
$
npx shadcn@latest add https://prism.icglabs.co/r/neon-map.jsonDependencies:maplibre-gl
View raw manifest →components/prism/NeonMap.tsx
"use client";
import { useEffect, useRef } from "react";
import maplibregl from "maplibre-gl";
import "maplibre-gl/dist/maplibre-gl.css";
/**
* NeonMap — a REAL interactive vector map (actual streets, pan/zoom/tilt) styled
* into a neon nightscape. Tiles + style come from OpenFreeMap (free, no API token,
* no signup). The base 'dark' style is recolored on load and a glowing great-circle
* arc + a pulsing pin are drawn on top. Separate WebGL context from R3F, so render
* it on its own (not stacked with the flight). The `.neon-pin` styles live in globals.css.
*/
function archedLine(a: [number, number], b: [number, number], n = 48, lift = 9) {
const pts: [number, number][] = [];
for (let i = 0; i <= n; i++) {
const t = i / n;
pts.push([a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t + Math.sin(Math.PI * t) * lift]);
}
return pts;
}
export function NeonMap({
center = [-122.42, 37.77],
zoom = 11,
}: {
center?: [number, number];
zoom?: number;
}) {
const ref = useRef<HTMLDivElement>(null);
const mapRef = useRef<maplibregl.Map | null>(null);
useEffect(() => {
if (!ref.current || mapRef.current) return;
const map = new maplibregl.Map({
container: ref.current,
style: "https://tiles.openfreemap.org/styles/dark", // free, no token
center,
zoom,
pitch: 55,
bearing: -18,
attributionControl: { compact: true },
});
mapRef.current = map;
map.on("load", () => {
const NEON = "#1fd4e6";
const MAGENTA = "#ff3d81";
try {
map.setPaintProperty("background", "background-color", "#05060a");
} catch {}
for (const layer of map.getStyle().layers ?? []) {
const id = layer.id;
const type = layer.type;
if (/water/.test(id) && type === "fill") {
try {
map.setPaintProperty(id, "fill-color", "#081018");
} catch {}
}
if (/(road|street|highway|transportation)/.test(id) && type === "line") {
try {
map.setPaintProperty(id, "line-color", NEON);
} catch {}
try {
map.setPaintProperty(id, "line-blur", 0.6);
} catch {}
}
if (/building/.test(id) && (type === "fill" || type === "fill-extrusion")) {
try {
map.setPaintProperty(id, type === "fill" ? "fill-color" : "fill-extrusion-color", "#0c1320");
} catch {}
}
}
// glowing arc
const arc: GeoJSON.Feature = {
type: "Feature",
properties: {},
geometry: { type: "LineString", coordinates: archedLine(center, [center[0] + 0.06, center[1] + 0.05]) },
};
map.addSource("arc", { type: "geojson", data: arc });
map.addLayer({
id: "arc-glow",
type: "line",
source: "arc",
paint: { "line-color": MAGENTA, "line-width": 4, "line-blur": 6, "line-opacity": 0.9 },
});
map.addLayer({
id: "arc-core",
type: "line",
source: "arc",
paint: { "line-color": "#ffffff", "line-width": 1.2 },
});
// pulsing pin
const el = document.createElement("div");
el.className = "neon-pin";
new maplibregl.Marker({ element: el }).setLngLat(center).addTo(map);
});
// resize when the container becomes visible / changes size
const ro = new ResizeObserver(() => map.resize());
ro.observe(ref.current);
return () => {
ro.disconnect();
map.remove();
mapRef.current = null;
};
}, [center, zoom]);
return <div ref={ref} className="h-full w-full" />;
}