Map

Places on an interactive map, to show a route, pick a place, drop a pin or lay out a multi-day itinerary.

Pattern

≈ 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

  1. Day 1 · Seattle
    1. Seattle, City
  2. Day 2 · Olympic National Park
    1. Port Angeles, Harbor town
    2. Hurricane Ridge, Scenic viewpoint
  3. Day 3 · Rain forest
    1. Hoh Rain Forest, Rain forest
  4. Day 4 · Pacific coast
    1. Ruby Beach, Beach
    2. Lake Quinault, Lake
  5. Day 5 · Astoria
    1. Astoria, Port town
  6. Day 6 · Oregon coast
    1. Cannon Beach, Beach
  7. Day 7 · Portland
    1. Portland, City
  8. Day 8 · Columbia River Gorge
    1. Multnomah Falls, Waterfall
  9. Day 9 · Mount Rainier
    1. Mount Rainier · Paradise, National park
  10. Day 10 · Back to Seattle
    1. 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: itinerary shows 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: view to show places, pick when the user must choose one of them, pin when they must point at a spot, itinerary for a trip over several days.
  • With route, give every place an order starting 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, send stops in visiting order with a day each and a kind when one fits. Pass route: { coordinates } only when a routing tool gave you a real path. Set selectable only 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, set fetchedAt and staleAfterMs.

Modes

ModeWhat the user doesResult
viewLooks. Markers and list rows open a popup with the place's detailsNone
pickSelects a place by its marker or its list row, then confirms{ action: "select", placeId }
pinClicks or taps the map to drop a pin, drags it to adjust, then confirms{ action: "pin", lat, lng }
itineraryFilters by day, browses stops on the map and in the card stripNone, 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, city stops, 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

app/api/chat/route.ts
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

PropTypeNotes
titlestring
mode"view" | "pick" | "pin" | "itinerary"Default view
places{ id, name, lat, lng, description?, category?, order? }[]Up to 25. Data-bound. Optional in pin mode
routeboolean | { 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
selectablebooleanitinerary only. Let the user choose a stop. Default false
center{ lat, lng }?Initial center. Omit to fit the map to the places
zoomnumber?Initial zoom with center, 1–19
submitLabelstring?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.

Source

On this page