Site embed
Use /siteembed/{mapId} when a destination website’s pages determine what its map shows. An article can show only the places it mentions, while a home page can show the whole destination with places from its tiles lifted out. Changing the POI set does not reload the iframe.
URL parameters
Section titled “URL parameters”| Parameter | Shape | Default | Meaning |
|---|---|---|---|
ns | String | default | Namespace required on every bridge message. |
pois | Comma-separated IDs | Empty | Sets the initial context. URL items start as mentioned markers; use setContext for exact roles. With no IDs, the whole map renders. |
mode | only, emphasize, all | all | Sets the initial display mode. |
showControls | true, false | true | Shows or hides navigation, geolocation, and recenter controls. |
showSearch | true, false | true | Shows or hides the search control. |
controlsPosition | top-left, top-right, bottom-left, bottom-right | bottom-right | Sets the map-control corner. |
fit | boundary, point | Unset: venue maps fit their boundary, other maps open at the map’s point | How the map opens when no lat/lng/z are given. boundary fits the whole boundary (padding 40 px, max zoom 18). |
lat, lng, z, b, p | Numbers | Map defaults | Set the initial latitude, longitude, zoom, bearing, and pitch. |
theme | light, dark | Unset | Reserved; currently a no-op. |
bg | Hex colour, # optional | Unset | Paints the map container and the style’s background layer (set, or inserted at the bottom) so the page colour shows until tiles load. URL-encode # as %23; invalid values are ignored. |
hideSignIn is accepted for compatibility, but the site embed has no sign-in UI.
SDK options
Section titled “SDK options”Wander Embed SDK v0.7.0 passes these options from Wander.siteMap(element, options) to the site embed URL:
| Option | Shape | Default | Meaning |
|---|---|---|---|
showControls | true, false | true | Shows or hides navigation, geolocation, and recenter controls. |
showSearch | true, false | true | Shows or hides the search control. |
controlsPosition | top-left, top-right, bottom-left, bottom-right | bottom-right | Sets the map-control corner. |
background | Hex colour, # optional | Unset | Sets the iframe URL’s bg value. |
fit | boundary, point | Unset: venue maps fit their boundary, other maps open at the map’s point | How the map opens when no center/zoom are given. boundary fits the whole boundary (padding 40 px, max zoom 18). |
center | { lng, lat } | Map defaults | Sets the initial longitude and latitude. |
zoom | Number | Map default | Sets the initial zoom when center is provided. |
When center is passed, the SDK still emits fit, but the child uses the URL coordinates (lng, lat, and optional z) instead.
Bridge protocol
Section titled “Bridge protocol”Host-to-child messages use this envelope:
{ source: 'wander-embed', v: 1, namespace, type, payload }Child-to-host messages use source: 'wander-embed-child'. Successful commands produce ack { type }. The protocol version remains 1: the site-embed verbs are additive, so an older embed simply ignores them.
Commands
Section titled “Commands”| Command | Payload | Description |
|---|---|---|
setContext | { items, mode?, fit?, padding? } | Replaces the previous item set, fetches only IDs that are not already loaded, and optionally fits the camera. Fetches are batched and chunked at 200. |
setMode | { mode } | Changes only the mode. |
setPadding | { top, right, bottom, left } | Sets and persists Mapbox camera padding. All four sides are required and must be finite numbers; a partial object or bare number is rejected. |
hover | { poiId: string | null } | Applies or clears the same hover state as pointer interaction. null clears it. |
fitBounds | { poiIds?, padding? } | Fits explicit IDs. Without IDs, fits the context, or all loaded POIs when the context is empty. |
flyTo | { poiId?, center?: { lng, lat }, zoom?, bearing?, pitch?, duration?, select? } | Moves the camera. center wins over poiId; select: true also opens the detail card. |
focus | { poiId? | poiIds?, fit? } | Sets the anchor set (one of poiId or a non-empty poiIds). Pushes onto the focus stack unless it equals the current set. Opens the card for a single anchor. fit: true fits anchors + access targets once edges load. |
back | {} | Pops the focus stack; the previous set is redrawn from cache. An empty stack clears the overlay. |
clearFocus | {} | Empties the stack, clears the overlay, closes the card. |
highlight | { poiId | null } | Highlights one target of the current anchors (pulse, label, fit anchor + target with the persisted padding). null clears. Neither selects nor re-anchors. |
select | { poiId, fitAssociations? } | Opens a POI’s card. With an empty stack it auto-anchors (v0.4 behaviour, fitAssociations still fits); with a stack it peeks and the overlay is unchanged. |
getAssociations | { poiId } | Replies with associations { poiId, items } from the child’s cache (fetching if needed) without selecting or rendering anything. |
clearSelection | {} | Clears the selected POI. |
Each setContext item has this shape:
{ poiId: 'poi-id', role: 'featured', geometry: 'marker', badge: '1'}role is required, is part of the protocol, and is either featured or mentioned. Both roles render as plain photo markers; the role does not change a POI’s appearance. badge is an optional string of 1–3 characters that renders in a numbered-style circle on the POI’s photo marker; longer values are ignored without dropping the item. For example: { poiId: 'poi-id', role: 'featured', badge: '1' }. Hosts can safely send badge to older /siteembed builds, where it is ignored. geometry is optional, accepts marker, path, or polygon, and is rarely needed because the embed resolves it. For repeated IDs, first-seen order is retained and the last value wins.
Events
Section titled “Events”| Event | Payload | Description |
|---|---|---|
loading | { namespace, mapId } | Fires as soon as the bridge mounts, before the map style loads. Show a skeleton on loading and hide it on ready. |
ready | { namespace, mapId, surface: 'siteembed', poiCount, mode } | Fires after Mapbox’s load event: the style has loaded and the first requested tiles have painted. On a satellite style over a slow connection this can take 30–60 seconds; dimensionChanged is not a readiness signal. |
ack | { type } | Confirms a successful command. |
contextApplied | { count, mode, missing: string[] } | Fires after every setContext and setMode, including empty contexts and non-empty missing. |
poiSelected | { poiId, source: 'host' | 'user' } | Reports a selected POI. host means a bridge verb caused it; user means a tap or card button. An older child omits source; treat undefined as user. |
poiDeselected | { source: 'host' | 'user' } | Reports that selection was cleared. host means a bridge verb caused it; user means a tap or card button. An older child omits source; treat undefined as user. |
poiHovered | { poiId } | Reports a hovered POI. |
poiUnhovered | {} | Reports that hover was cleared. |
clusterClicked | { count, bounds: { n, s, e, w } } | Reports a clicked cluster and its bounds. |
associationHighlighted | { fromPoiId, toPoiId, type, source } | A target was highlighted by a tap (source: 'user') or by the highlight verb ('host'). |
focusChanged | { poiIds, depth, reason } | The anchor set changed: reason is focus, back or clear. Never fires for a peek. |
searchPerformed | { query, mapResultCount, externalResultCount, partial } | Reports a search. partial: true means Google Places was unavailable, so only on-map results were shown. |
searchResultSelected | { query, poiId } | The visitor picked an on-map result. The usual poiSelected { source: 'user' } follows. |
externalPlaceSelected | { query, placeId, name, address?, lng, lat, photo?, rating?, source: 'google' } | The visitor picked a place that is not on this map. The embed pins it and shows a small card with its name, address, and Directions; the host owns any “add to trip” action. |
externalPlaceCleared | {} | The external pin was dismissed by the card ×, a background tap, a new pick, setContext, or setMode. |
associationsLoaded | { poiIds, poiId, count, byCategory } | Fires once per anchor set when its associations resolve (poiId is the first anchor, kept for v0.4 hosts). byCategory counts access, practical, safety, enhancement, sequence, alternative, context. |
associations | { poiId, items } | Reply to getAssociations. Items are the raw association rows, including direction: 'reverse' edges. |
associationSelected | { fromPoiId, toPoiId, type } | Fires when a chip on the map or a row in the detail card is tapped. The tap peeks: toPoiId’s card opens, the anchor’s overlay stays. |
error | { scope: 'associations', poiId } | The association fetch failed. The detail card still renders; aborted fetches are silent. |
boundsChanged | { center: { lng, lat }, zoom, bounds: { n, s, e, w } } | Reports a changed viewport. |
dimensionChanged | { width, height } | Reports dimensions. Height is a recommendation derived from width, not a measured document height. |
For selection events, host means select, focus, back, clearFocus, flyTo with select: true, clearSelection, setContext, or setMode caused the change. user means a tap or a card button caused it.
Associations — anchor and peek
Section titled “Associations — anchor and peek”The overlay follows the anchor set, not the selection. focus sets the anchors and their edges stay drawn until back, clearFocus, setContext or setMode. Targets are drawn by decorating their own pin — a ring in the category colour, a badge with the type glyph, a label — never as a second marker; a target outside the loaded feed falls back to a standalone chip. A first tap on a decorated pin or a card row highlights it (pulse, label, camera fits anchor + target); a second tap or the row’s Details › opens the peek. Opening a card for a non-anchor (select while anchored, a second tap on a target, a marker click while anchored) is a peek: the overlay does not change, the peeked chip is highlighted, and the card offers Focus here (push) and Back to ‹anchor›. Opening any card while nothing is anchored auto-anchors that POI. Anchors sit on a stack; back pops it and redraws from cache without a request. One request per anchor set, covering only ids not already cached.
Every decorated target carries a label: display_label trimmed to 36 characters at a word boundary, else the target’s name; a navigational target without a label reads “Park here” / “Start here”. Below zoom 12 only navigational and peeked targets keep their labels; above it every label is shown subject to collision.
| Category | Types | Map treatment |
|---|---|---|
access | trailhead, parking, entrance, accessible_entrance, visitor_center, transit_stop, rideshare_dropoff, boat_launch | forest-green ring + badge and a dotted connector from the anchor; a path target with geometry is drawn as its own highlighted line; the navigational target gets a larger chip and the card’s “Directions to the lot” button |
practical | food_nearby, water_refill, restroom, ticket_purchase, gear_rental | ink ring + badge |
safety | emergency_exit, cell_signal, meeting_point, ranger_station | ink ring + badge |
enhancement | best_viewpoint, photo_spot, hidden_gem, insider_tip, sunrise_spot | amber ring + badge |
sequence | do_before, do_after, pairs_with | creek-blue ring + badge |
alternative | when_closed, rainy_day, less_crowded, similar_vibe | slate ring + badge |
context | historically_connected, part_of_complex, featured_in | rust ring + badge; polygon targets are outlined |
The detail card lists the same edges in that order, skipping empty groups, and tapping a row selects the associated POI.
Display modes
Section titled “Display modes”| Mode | Context POIs | Non-context POIs |
|---|---|---|
only | photo markers, clustered | not rendered |
emphasize | photo markers, clustered | smaller category pins, full opacity, no labels |
all | photo markers, clustered | normal-size category pins with labels |
Context POIs render as photo markers rather than category icons. Their image is resolved in order from poi.firstPhoto, the map’s logo, and /no-image-placeholder.png. Many POIs have no photo, so the placeholder path is common rather than exceptional.
Photo markers cluster. A context cluster source holds one point per context POI. POIs that are unclustered at the current zoom render as photo markers, while the rest collapse into a cluster bubble styled like every other cluster on the map. Clicking one emits clusterClicked.
Selection breaks a POI out of its cluster. A selected context POI always renders its photo marker, enlarged and with its title shown, even while its cluster stays collapsed. Selection is the only emphasised state.
Context IDs are always removed from the category sources, so each POI has exactly one renderer and one interaction path. The selected-POI card remains available in every mode.
Search
Section titled “Search”At iframe widths of 640 px or more, the search control sits at the top right. When controlsPosition=top-right, it moves to the top left. Below 640 px it becomes a full-width strip. The control honours the camera padding set with setPadding.
Results are grouped into On this map and Elsewhere. On this map contains Wander POIs belonging to this map. Elsewhere contains Google Places that do not match a map POI; a Google place inside the map boundary is still Elsewhere when it is not a POI on the map.
Selecting an external place creates page state: the embed shows one external pin at a time with a small card, and clears it on setContext, setMode, the card’s ×, a background tap, or a new pick. The embed never adds the place to a trip or itinerary; the host can own that action by handling the event:
site.on('externalPlaceSelected', (e) => addToTrip({ id: 'google:' + e.placeId, name: e.name, photo: e.photo ?? null, category: e.address ?? 'Outside the map' }));Capability and local testing notes
Section titled “Capability and local testing notes”isConnected() only shows that the child answered ready; it does not prove that the embed understands the site-embed verbs. After calling setContext, listening for contextApplied is the only reliable capability check.
A page opened from file:// has the opaque origin "null". The embed guards its acknowledgements accordingly, so local demo harnesses work.
Host checklist
Section titled “Host checklist”- Mount once, navigate content around it — keep the iframe in a layout that survives client-side navigation; each page calls
setContext. - Paint the frame before the map paints — use
background/bgandfill: truefor full-bleed maps; show a skeleton onloadingand hide it onready. - Use four-sided padding on every layout change — call
setPadding({ top, right, bottom, left })for panel, sheet, and mini-card changes, and use the same padding insetContext’spadding; partial objects and bare numbers are rejected. - Use
contextAppliedas the capability probe, notisConnected(); surfacemissing. - Act on
poiSelectedonly whensource === 'user'; treat a missingsourceasuser. setContextandsetModereset the page (focus stack and selection); sendselect/focusafter them.- Route on
associationSelected; the child only peeks. - Handle
externalPlaceSelectedif your site has a trip or itinerary; the embed only pins and reports.