Components
registry.unmap.dev is a shadcn-format component registry for unmap
maps. Components are copied into your project, not installed as a package, so you own the source
and can edit it freely. They install the way every shadcn component does: register the @unmap
registry once, then add by name. Files land in components/ui/ and lib/ beside your own, and
the CLI asks before overwriting anything.
npx shadcn@latest registry add @unmap=https://registry.unmap.dev/r/{name}.json
npx shadcn@latest add @unmap/mapnpx shadcn-vue@latest add https://registry.unmap.dev/r/vue/map.jsonThe React line registers the registry once per project, so later items can be named @unmap/<item>; shadcn-vue has no registry add and takes the item URL directly. Files land in components/ui/ beside your own; nothing is overwritten without asking.
Browse every item on registry.unmap.devThere are two lanes, one per CLI, with the same items:
| Lane | CLI | What you get |
|---|---|---|
| React | shadcn | .tsx components in components/ui/. The geocoder and routing panel are your own shadcn Combobox, Field, Input, Button and Alert. |
| Vue | shadcn-vue | Unmap*.vue components, one directory per item under components/ui/, on your shadcn-vue Combobox, Field, Input, Button and Alert. |
Each family of items has its own page:
| Page | Items |
|---|---|
| Map and markers | map-context, map-themes, map, marker-popup, map-controls |
| Data layers | map-layers, route-line, geojson-layer, cluster-layer, heatmap-layer, arc-layer, flow-layer, layer-legend |
| Search and routing | geocoder, routing-panel, route-steps |
| Overlay components | energy-layer-control, energy-map, well-search, legal-land-search, energy-popup, treaty-context |
| Map tools | measure, feature-inspector, time-slider, draw, compare-swipe, elevation-profile, api-calls, api-inspector |
| Blocks | store-locator, directions, flow-map, elevation-explorer, delivery-tracker, census-choropleth, land-treaty-inspector, address-playground, energy-explorer |
Start a new project
@unmap/cli scaffolds a shadcn or shadcn-vue project with the components already in it:
npx @unmap/cli create my-app --key um_live_...It runs npx shadcn init (or shadcn-vue init for --framework nuxt and vue) with your
template and preset, registers @unmap, adds the four components, writes the key to the
framework's public env file, and replaces the home page with a map, a search box and a pin.
Pass --theme <code> from /create and it asks whether markers and routes take the
theme's accent or your design system's --primary.
Add to an existing project
npx @unmap/cli add map geocoderadd reads your package.json to pick the lane, writes the @unmap entry into components.json
itself if it is not there yet, and hands off to the shadcn CLI. By hand it is two commands on
React and one on Vue and Nuxt:
npx shadcn@latest registry add @unmap=https://registry.unmap.dev/r/{name}.json
npx shadcn@latest add @unmap/map @unmap/geocoder @unmap/routing-panel @unmap/marker-popupnpx shadcn-vue@latest add https://registry.unmap.dev/r/vue/map.jsonshadcn-vue has no registry add command yet. To write @unmap/map instead of a URL, add the
registry to components.json once (unmap add does this for you):
{ "registries": { "@unmap": "https://registry.unmap.dev/r/vue/{name}.json" } }npx shadcn-vue@latest add @unmap/map @unmap/geocoder @unmap/routing-panel @unmap/marker-popupEvery map-bound component depends on map-context, so adding one fetches it. The geocoder brings
the official Combobox; the routing panel brings Field, Input, Button and Alert. The React items
expect a shadcn project on Base UI, the base-nova style that
npx shadcn init --base base --preset nova sets up and unmap create uses: the geocoder is Base
UI's Combobox, and the map controls hand your Button to the Tooltip through Base UI's render
prop rather than Radix's asChild. Preview a change with --dry-run before it touches a file (shadcn also takes --diff; shadcn-vue 2.8 supports
neither yet, so on the Vue lane unmap add --dry-run only prints what it would run).
npx shadcn search @unmap (or npx shadcn-vue search @unmap, and view @unmap/map for
one item's detail) lists every item once the namespace is registered.
React
import { Map } from "@/components/ui/map";
import { Geocoder } from "@/components/ui/geocoder";
import { Marker, Popup } from "@/components/ui/marker-popup";
import { MAP_THEMES } from "@/lib/map-themes";
export default function Demo() {
const apiKey = process.env.NEXT_PUBLIC_UNMAP_KEY!;
return (
<div style={{ height: 480 }}>
<Map apiKey={apiKey} center={[-114.07, 51.05]} zoom={11} theme={MAP_THEMES.boreal} accent="system">
<div style={{ position: "absolute", top: 12, left: 12, width: 280, zIndex: 10 }}>
<Geocoder apiKey={apiKey} />
</div>
<Marker lngLat={[-114.07, 51.05]}>
<Popup>Downtown Calgary</Popup>
</Marker>
</Map>
</div>
);
}The map those components mount is the one @unmap/maps builds, and it looks like this:
import { Unmap } from "@unmap/sdk";
const unmap = new Unmap({
key: "um_live_...",
container: "map",
style: "base",
mode: "light",
lang: "en",
center: [-114.07, 51.05],
zoom: 11,
});If you would rather not copy components, @unmap/react and @unmap/next provide the same map as
hooks and a ready-made component. See the Overview.
Vue and Nuxt
The Vue lane installs through shadcn-vue into a project that has run npx shadcn-vue init.
Files land one directory per item, each with an index.ts: components/ui/map/UnmapMap.vue,
geocoder/UnmapGeocoder.vue, routing-panel/UnmapRoutingPanel.vue, marker-popup/UnmapMarker.vue,
UnmapPopup.vue, UnmapMarkerTooltip.vue and UnmapMarkerLabel.vue, plus lib/map-context/index.ts,
under src/ or Nuxt 4's app/ as your aliases say. The import is @/lib/map-context.
<script setup lang="ts">
import { UnmapMap } from "@/components/ui/map";
import { UnmapGeocoder } from "@/components/ui/geocoder";
import { UnmapMarker, UnmapPopup } from "@/components/ui/marker-popup";
const apiKey = import.meta.env.VITE_UNMAP_KEY;
</script>
<template>
<div style="height: 480px">
<UnmapMap :api-key="apiKey" :center="[-114.07, 51.05]" :zoom="11" theme="u4e" accent="system">
<div style="position: absolute; top: 12px; left: 12px; width: 280px; z-index: 10">
<UnmapGeocoder :api-key="apiKey" />
</div>
<UnmapMarker :lng-lat="[-114.07, 51.05]">
<UnmapPopup>Downtown Calgary</UnmapPopup>
</UnmapMarker>
</UnmapMap>
</div>
</template>UnmapMap takes the style as map-style ("base", "muted", "outdoor", "blueprint", "blush", "orchid", "canopy", "lagoon", "tropic", "sunset", "bold", "pastel", "atlas" or "swiss") so the plain
style attribute keeps styling the container; class and style fall through to it. It also
accepts mode ("light" or "dark") and lang ("en" or "fr"). Component names keep the
Unmap prefix because <map> is an HTML element. On Nuxt, shadcn-nuxt reads each directory's
index.ts and auto-imports the components as <UiUnmapMap>, <UiUnmapGeocoder> and so on; set
shadcn: { prefix: "" } in nuxt.config.ts to use them as <UnmapMap>.
Not on shadcn-vue? @unmap/vue and @unmap/nuxt provide the map, geocoder and routing as
composables for any UI. See the Overview.
Styling and theming
The registry ships no theme and no brand colour. Components take on your design system:
- The map container uses
rounded-lgandbg-muted, so it follows your--radiusand palette. - The geocoder and routing panel are your own shadcn primitives.
- MapLibre's popups, zoom and compass buttons, attribution and scale bar are restyled onto
--popover,--background,--border,--radius,--ringand--muted-foregroundby CSS themapitem adds to your stylesheet on install, scoped under[data-unmap-map]so other maps in your app are untouched; popups and controls take the page's font throughfont: inherit. The chrome follows your.darkclass. - Markers and route lines default to your
--primary, resolved to a concrete colour when they are drawn and again after every style swap, because MapLibre paint cannot readvar(). - The zoom and compass buttons are on by default, at
top-right.<Map controls={false}>takes them off, and<Map controls={{ position: 'bottom-left' }}>moves them. The attribution stays either way. For buttons on your own primitives, addmap-controls, described in Map and markers. - The map is a globe by default, flattening into Mercator as it zooms in.
<Map projection="mercator">draws the flat map at every zoom. - The basemap follows your page's light or dark mode: a
darkorlightclass on<html>, thendata-theme, thencolor-scheme, then the reader's system setting.<Map mode="light">ormode="dark"pins it. Switching mode, style, theme orlangswaps the style in place, so the camera, markers, popups and a drawn route stay where they are. - Layers you add yourself should add themselves again when
useMapGeneration()changes, or whenuseMapReady()turns true again: a swap removes every source and layer. center,zoom,bearingandpitchmove the camera in place. PassonViewportChange(Vue:@viewport-change) and feed the values back for a controlled map; moves caused by these props are not reported back, so it cannot loop.- Your
Spinnercovers the map until its first style loads.<Map loading>shows it on demand, for example while your own data loads.
Cartography is separate from UI chrome. The theme prop takes a short code such as u4e or a full
ut1. code from the theme builder; that code carries its own marker and route colours.
<Map accent="theme" | "system"> says which wins: the default is "theme" when a theme is set
and "system" otherwise, and the export dialog on /create asks you before it writes
the snippet. An explicit color or lineColor prop always wins over both. See
Maps API.
Every component marks its root with a data-unmap-map, data-unmap-geocoder,
data-unmap-routing or data-unmap-legend attribute, and every popup carries the unmap-popup
class, as hooks for your own CSS.
Templates and examples
Map templates are complete React experiences distributed as registry:block items. They copy editable components and English/French sample data into your app, install their dependencies, and inherit your shadcn theme. The four templates use Base UI shadcn primitives.
Once the @unmap namespace above is configured:
npx shadcn@latest add @unmap/business-locator-01
npx shadcn@latest add @unmap/local-guide-01
npx shadcn@latest add @unmap/public-notices-01
npx shadcn@latest add @unmap/map-story-01Or with the unmap CLI, which also works for the examples: npx @unmap/cli add business-locator-01.
You can also install directly, without namespace configuration:
npx shadcn@latest add https://registry.unmap.dev/r/business-locator-01.jsonimport { BusinessLocator } from "@/components/unmap-business-locator"
<BusinessLocator apiKey="um_live_..." />Pass your browser key and allow your site's origin on it. Use lang="fr" for French. Edit lib/business-locator-01-data.ts to replace the sample places, and the copied components to change the layout. The other exports are LocalGuide, PublicNotices and MapStory, in components/unmap-local-guide.tsx, components/unmap-public-notices.tsx and components/unmap-map-story.tsx.
MapStory follows the reader's scroll, with its chapters in lib/map-story-01-data.ts: as each chapter reaches the middle of the screen the camera flies to it, and a chapter with a route or travel-time areas asks the routing API for them once, the first time it opens (one request each). LocalGuide steps through its stops with Previous stop and Next stop buttons instead.
Smaller examples ship the same way: one item each, React-only, installed with npx @unmap/cli add <name>. They are listed in the gallery and shown beside each item on its family page, such as Map tools.
Templates and examples ship in the React lane only; the page blocks install in both. The business locator, local guide and public notices also have standalone HTML starters and GeoJSON downloads on their template pages.
For agents
registry.unmap.dev/llms.txt indexes every item for coding
agents, and each item has a Markdown page with its install command, props and a working example:
registry.unmap.dev/llm/<item>.md for React and registry.unmap.dev/llm/vue/<item>.md for Vue
and Nuxt. npx @unmap/cli add prints the pages for the items it installs. A prompt that works in
a project with a components.json:
Read https://registry.unmap.dev/llms.txt. Then run npx @unmap/cli add map in this project and render the map on the home page, centred on Calgary, with my unmap key from the environment.Next steps
- Browse every item with install commands at registry.unmap.dev.
- Quickstart for the plain-JavaScript path with
@unmap/sdk. - Geocoding API and Routing API for what the geocoder and routing panel call under the hood.
- Energy for
energy-map,well-search,energy-popup, andenergy-layer-control.