Map
Places on an interactive map, to show a route, pick a place, drop a pin or lay out a multi-day itinerary.
≈ 2,339 tokens per use (tool 1,291 · guidance 306 · typical payload 742)How token costs are estimated
Example: Map
Plan a 10-day Pacific Northwest road trip.
Your 10-day Pacific Northwest road trip
- Day 1 · Seattle
- Seattle, City
- Day 2 · Olympic National Park
- Port Angeles, Harbor town
- Hurricane Ridge, Scenic viewpoint
- Day 3 · Rain forest
- Hoh Rain Forest, Rain forest
- Day 4 · Pacific coast
- Ruby Beach, Beach
- Lake Quinault, Lake
- Day 5 · Astoria
- Astoria, Port town
- Day 6 · Oregon coast
- Cannon Beach, Beach
- Day 7 · Portland
- Portland, City
- Day 8 · Columbia River Gorge
- Multnomah Falls, Waterfall
- Day 9 · Mount Rainier
- Mount Rainier · Paradise, National park
- Day 10 · Back to Seattle
- Snoqualmie Falls, Waterfall
When to use
- Where things are matters to the decision: viewings across a city, hotels near a venue, a route between stops.
- The user needs to choose a place, or tell you a location by pointing at it, like a pothole or a meeting point.
- A trip over several days:
itineraryshows the stops by day on a route, with a card per place.
When not to use
- There's one address. Write it out.
- Location doesn't affect the decision. Use a list or a Data Table.
- You don't have coordinates from a tool. Never guess latitude or longitude.
How the agent should use it
- Take coordinates from a tool result, never from memory.
- Pick the mode for the job:
viewto show places,pickwhen the user must choose one of them,pinwhen they must point at a spot,itineraryfor a trip over several days. - With
route, give every place anorderstarting at 1. Markers are numbered and joined by a line in that order. - Keep descriptions to the one fact that helps the choice: price, distance, time.
- In
itinerary, sendstopsin visiting order with adayeach and akindwhen one fits. Passroute: { coordinates }only when a routing tool gave you a real path. Setselectableonly when you need the user to choose a stop. - On
cancel, do nothing and ask what they'd like instead. - Lifecycle: places and trips don't expire. If ratings or opening states (a stop's
hours) come from live data, setfetchedAtandstaleAfterMs.
Modes
| Mode | What the user does | Result |
|---|---|---|
view | Looks. Markers and list rows open a popup with the place's details | None |
pick | Selects a place by its marker or its list row, then confirms | { action: "select", placeId } |
pin | Clicks or taps the map to drop a pin, drags it to adjust, then confirms | { action: "pin", lat, lng } |
itinerary | Filters by day, browses stops on the map and in the card strip | None, or { action: "select-stop", stopId } with selectable |
pick, pin and a selectable itinerary can also return { action: "cancel" }.
Itinerary
The map fills the card. Stops are joined in order by a line (straight legs, or route.coordinates when given) and drawn as round markers with an icon for their kind. A strip of place cards sits on the map: thumbnail, name, rating and category, then the day and opening state.
- Day filter: "All locations" or one day. Picking a day fits the map to that day's stops, dims the others and highlights the day's leg, including the drive in from the previous stop. The strip shows that day's cards.
- Selecting a stop: click its marker or its card. The card scrolls into view, the marker grows and the map eases to it (it jumps with reduced motion). Clicking a dimmed stop switches the filter to its day.
- Fit all stops: the round button next to the filter brings every stop back into view. It never asks for the user's location.
- Expand: opens the same map fullscreen. Escape or Close returns to the card.
- Labels: to keep the map readable, names show only for the selected stop,
citystops, and the trip's first and last stop. Every other stop is named on its card. - Result: display only by default; selection stays local. With
selectable, the user confirms a stop and the agent receives{ action: "select-stop", stopId }.
Accessibility
The list below the map is the full alternative to the map: every place is listed with its name and description, and in pick mode each row has its own select button. Markers are buttons named after their place, so they can be reached with Tab. In pin mode, "Drop pin at map center" places the pin without a pointer (pan the map with the arrow keys first), the pin moves with the arrow keys (Shift for bigger steps), and its coordinates are shown as text.
In itinerary mode the markers are hidden from assistive tech. A visually hidden ordered list gives every stop by day, with its category, rating and opening state. The card strip is a labelled region of buttons: Tab reaches it, the arrow keys (and Home/End) move between cards, and each card reports whether it's selected. Day changes are announced. The fullscreen view is a modal dialog that keeps focus inside and returns it to Expand when it closes.
Map tiles
The map uses MapLibre GL with CARTO's free Positron and Dark Matter styles, which need no API key. It follows the page's .dark class, and the attribution stays visible as the tile provider requires. Scroll-wheel zoom needs Ctrl or ⌘ so the map doesn't trap page scrolling in a chat.
MapLibre runs its tile parsing in a web worker. Bundlers don't copy that worker for you, so the component points MapLibre at the matching published build on jsDelivr. To serve it yourself, copy node_modules/maplibre-gl/dist/maplibre-gl-worker.mjs to your public folder and call setWorkerUrl("/maplibre-gl-worker.mjs") from maplibre-gl before the map renders.
Installation
npx shadcn@latest add https://your-site.com/r/map.json
Usage
import { tool } from "ai"
import { mapTool } from "@/components/genui/map.schema"
const tools = {
[mapTool.name]: tool({
description: mapTool.description,
inputSchema: mapTool.inputSchema,
}),
}In view mode, and in itinerary without selectable, the map is the whole answer, so give the tool an execute that returns immediately. In pick, pin and selectable itinerary modes, the agent receives:
{ action: "select", placeId: "casa-tejo" }
{ action: "pin", lat: 38.72683, lng: -9.13518 }
{ action: "select-stop", stopId: "hoh" }
{ action: "cancel" }With LangGraph
Push it from a graph node with ui.push({ name: "show_map", props: { toolCallId, ...args } }, { message }), and render the thread's UI messages with the Renderer.
Restoring from history
Pass the stored tool output as result. The map restores the selected place or the pin, locks, and never calls onResult again:
<MapView
{...part.input}
result={part.state === "output-available" ? part.output : undefined}
/>Schema
| Prop | Type | Notes |
|---|---|---|
title | string | |
mode | "view" | "pick" | "pin" | "itinerary" | Default view |
places | { id, name, lat, lng, description?, category?, order? }[] | Up to 25. Data-bound. Optional in pin mode |
route | boolean | { coordinates?: [lng, lat][] } | true: number the markers and join them in order. An object follows a detailed path. Default false |
stops | { id, name, lat, lng, day?, kind?, image?, rating?, category?, hours?, url? }[] | itinerary only. Up to 40, in visiting order. Data-bound |
days | { day, label? }[] | itinerary only. Labels for the day filter |
selectable | boolean | itinerary only. Let the user choose a stop. Default false |
center | { lat, lng }? | Initial center. Omit to fit the map to the places |
zoom | number? | Initial zoom with center, 1–19 |
submitLabel | string? | Confirm button for pick, pin and selectable itinerary |
A stop's kind is one of beach, mountain, city, forest, lake, waterfall, harbor, museum, food, hotel, viewpoint or activity; each has its own marker icon. image is { src, alt } with an http(s) or data:image URL, and url must be http(s). A stop's hours is its opening state, like "Open" or "Closes 6 PM".
Host props: status (streaming, ready, submitted, cancelled), onResult, result and className.