Skip to content

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.

Copy this component into your project (map)
React
npx shadcn@latest registry add @unmap=https://registry.unmap.dev/r/{name}.json
npx shadcn@latest add @unmap/map
Vue and Nuxt
npx shadcn-vue@latest add https://registry.unmap.dev/r/vue/map.json

The 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.dev

There are two lanes, one per CLI, with the same items:

LaneCLIWhat you get
Reactshadcn.tsx components in components/ui/. The geocoder and routing panel are your own shadcn Combobox, Field, Input, Button and Alert.
Vueshadcn-vueUnmap*.vue components, one directory per item under components/ui/, on your shadcn-vue Combobox, Field, Input, Button and Alert.
ItemWhat it is
mapThe map display component. Mounts a container, creates a MapLibre map on the unmap gateway, and shares it with its children. Restyles MapLibre's popups and controls onto your tokens.
geocoderA search box with debounced autocomplete that flies the map to the chosen place.
routing-panelFrom/to inputs that resolve both addresses, request a route, and draw it on the map.
marker-popupDeclarative markers and popups positioned on the map.
map-themesNamed short theme presets (u00 and up) for the map's theme prop. Compose your own at unmap.dev/create.
map-contextThe shared map store the other items depend on. Installed automatically.
energy-map<Map> already composed with profile="energy" (muted by default). Map composition only, not a style.
energy-layer-controlToggles the energy overlay set on a map that already used profile="energy".
well-searchSearch box labelled for UWIs. Sends the query string only; the gateway detects UWI vs civic.
legal-land-searchSearch box labelled for LSD. Same rule: query string only.
energy-popupIdentify wells, pipelines, and facilities on click.

The energy items pair with profile="energy". See Energy. legal-land-search is the LSD twin of well-search.

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 geocoder

add 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-popup

shadcn-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-popup

Every 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. 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:

live result
the code that produced it
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 and UnmapPopup.vue, plus lib/map-context/index.ts, under src/ or Nuxt 4's app/ as your aliases say. The import stays @/lib/map-context. Upgrading from the flat components/ui/Unmap*.vue layout? Delete those files after re-adding; the new directories sit beside them. Also delete any lib/vue/ directory or flat lib/map-context.ts file left by an install from before 2026-09-07: a flat file shadows the new lib/map-context/index.ts, since TypeScript and Vite resolve the file before the directory index.

<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" or "pastel") 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-lg and bg-muted, so it follows your --radius and 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, --ring and --muted-foreground by CSS the map item 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 through font: inherit. Dark mode follows your .dark class.
  • Markers and route lines default to your --primary, resolved to a concrete colour when they are created, because MapLibre paint cannot read var().
  • 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.
  • <Map projection="globe"> draws the map as a sphere. The default is mercator, the flat map.

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 or data-unmap-routing attribute, and every popup carries the unmap-popup class, as hooks for your own CSS.

Next steps