Skip to content

Embed SDK

wander-embed.js creates a list embed or a site embed and gives your page a JavaScript API for controlling it and receiving events.

For a production site, load a versioned build rather than the moving wander-embed.js, and add its integrity hash so the browser refuses a file that does not match:

<script
src="https://developer.wandermaps.com/sdk/wander-embed-0.7.0.js"
integrity="sha384-1CrKLeuX+qqz/SrIRDqWcSk7XrugrOvNZkQxMAwIKcIyOFVrTb7dw8g3tdw+YcFH"
crossorigin="anonymous"></script>

Versioned files are never modified after release. You can also copy one into your own build and self-host it; nothing in the SDK depends on being served from this domain. Wander.VERSION reports the SDK version at runtime and Wander.PROTOCOL the wire protocol.

The list of releases, with hashes, is in the changelog and at https://developer.wandermaps.com/sdk/versions.json.

If the SDK is unavailable for any reason, the iframe embeds work without it, so a host can fall back to a plain <iframe> with the same mapId and parameters.

After loading the preview SDK, pass a selector or DOM element and a mapId:

<div id="wander-map"></div>
<script>
const map = Wander.map('#wander-map', {
mapId: 'wake-county',
pois: [
'f56b236a-1bb8-4afe-a302-d84b564a4cf6',
'99f4dce3-dcd6-40aa-a297-6411cb8a7fd9'
],
namespace: 'weekend-guide',
allowGeolocation: true
});
</script>

The SDK targets https://web.wander-app.com/listembed/{mapId}.

OptionDefaultDescription
mapIdRequiredIdentifies the Wander map.
originhttps://web.wander-app.comSets the embed origin.
namespacedefaultIsolates this embed’s messages and maps to the ns URL parameter.
pois[]Supplies an array of POI IDs.
daysNoneSupplies day numbers positionally aligned with pois.
stopsNoneSupplies the itinerary as [{ poiId, day }] in visit order; overrides pois and days. Repeated IDs are separate stops.
listIdNoneLoads a saved list and maps to the sb URL parameter.
routeNoneEnables full route mode.
routeTypedrivingSets driving, walking, or cycling routing when provided.
showNumbersfalseShows numbered marker badges when enabled.
showAllPoistrueShows other POIs from the map when enabled.
showControlstrueShows map controls when enabled.
controlsPositionbottom-rightPositions controls at a supported map corner.
zoomAuto-fitSets the initial zoom. Applied only alongside center — see the note below.
centerAuto-fitSets the initial center as { lng, lat }.
titleWander mapSets the iframe’s accessible title.
height420Sets the initial iframe height in pixels.
autoResizetrueApplies the embed’s recommended height when it changes.
allowGeolocationfalseAdds iframe geolocation permission when enabled; use true for accurate visitor analytics.

An explicit camera in the URL suppresses the embed’s auto-fit, so a zoom with no center opens the map on its default centre with your POIs out of frame. The SDK therefore only sends camera parameters when center is present; to open on a particular place, pass both, or let the embed fit the POIs and call flyTo once it is ready.

MemberDescription
flyTo(options)Moves the camera using poiId, center, zoom, bearing, pitch, or duration. Camera only — pass select: true to also open the POI’s detail popup.
select(poiId)Selects a POI.
clearSelection()Clears the selected POI.
setActiveDay(day)Shows one itinerary day; pass null to clear the day filter.
fitBounds()Fits the map to its current POIs.
setPois(pois, days)Replaces the POIs and optional aligned days, reloading the iframe. Accepts [{ poiId, day }] stops too, and returns immediately when the set is unchanged.
setStops(stops)Replaces the itinerary with [{ poiId, day }] stops. Alias of setPois.
getItinerary()Returns the most recent itineraryChanged payload, or null before one arrives.
getActiveDay()Returns the active day number, or null when every day is shown.
getUrl()Returns the embed URL the SDK is currently rendering.
on(type, handler)Subscribes to an SDK event.
off(type, handler)Unsubscribes a handler from an SDK event.
isConnected()Returns whether the iframe has completed its ready handshake.
destroy()Removes the iframe, listener, queued commands, and event handlers.
.frameReferences the generated iframe element.

Commands sent before ready are queued and flushed after the iframe connects, so you do not need to guard against an initialization race.

Wander.embedUrl(options) builds the same URL without mounting anything, for pages that render their iframe server-side.

map.on('poiSelected', ({ poiId }) => {
console.log('Selected POI:', poiId);
});
EventPayloadDescription
ready{ namespace, mapId, poiCount, maxDay }Fires once the map is ready to accept commands. It fires before the POI fetch resolves, so read the itinerary from itineraryChanged rather than from this payload.
itineraryChanged{ poiCount, maxDay, poiIds, days }Reports the itinerary the embed actually loaded, where days is [{ day, poiIds }]. Fires when the data lands and again whenever the set changes. An empty itinerary is never reported.
dayChanged{ day, maxDay }Reports the active day, null for all days. Fires for the embed’s own day chips as well as setActiveDay.
dimensionChanged{ width, height }Reports the embed’s recommended dimensions.
poiSelected{ poiId }Fires when a POI is selected.
poiDeselected{}Fires when the current POI is deselected.
boundsChanged{ center, zoom, bounds: { n, s, e, w } }Reports a changed viewport.
errorReserved — not currently emittedReserved for future SDK errors.

When autoResize is true, the SDK applies the height from dimensionChanged to the iframe automatically. Messages include the configured namespace, so multiple embeds can coexist on the same page without receiving one another’s commands or events.

ready, itineraryChanged, and dayChanged describe state rather than a moment, so a handler subscribed after one has already fired still receives the last payload. The other events are only delivered live.

Set autoResize: false when your own layout owns the map’s height — a drawer or flex container, for example — and size the iframe yourself through map.frame.style.

For end-to-end integrations built on these events, see Itineraries & listing maps.

Wander.siteMap creates a site embed whose context can change without reloading its iframe:

const map = Wander.siteMap('#el', {
mapId, namespace, mode, context, padding,
showControls, controlsPosition, center, zoom, origin, allowGeolocation,
fill, lazy, background
});
OptionDefaultDescription
fillfalsePositions the iframe absolute; inset: 0; width/height 100% inside your container and never lazy-loads it. Ignores height.
lazytrueSets loading="lazy" on the iframe. Pass false for above-the-fold or full-bleed maps.
backgroundUnsetA hex colour (#RGB / #RRGGBB). Painted on the iframe element and sent to the child as bg, so your page colour shows until the first tiles paint.
const site = Wander.siteMap(el, { mapId, fill: true, background: '#ECE5D6' });
site.on('loading', () => showSkeleton());
site.on('ready', () => hideSkeleton());
site.on('poiSelected', ({ source }) => { if (source === 'user') closeSheet(); });
MemberDescription
setContext(items, { mode, fit, padding })Replaces the context without changing frame.src.
setMode(mode)Changes the display mode.
setPadding(p)Sets the camera padding.
hover(poiId | null)Applies or clears the hover state.
fitBounds(ids?)Fits explicit IDs, or the current context when IDs are omitted.
flyTo(options)Moves the camera.
focus(poiId | poiIds, { fit })Makes one POI (or a set) the anchor: its association edges are drawn and stay drawn. Pushes onto the focus stack; focusing the same set again does not push. fit: true fits the anchors and their access targets once the edges load.
back()Pops the focus stack and redraws the previous anchor set from cache. Clears the overlay when the stack empties.
clearFocus()Empties the stack and removes the overlay.
getFocus()Returns the last focusChanged payload, or null.
highlight(poiId | null)Highlights one association target of the current anchors: its pin pulses, its label shows, and the map fits the anchor and that target together. null clears. Does not select or re-anchor.
select(poiId, { fitAssociations })Opens a POI’s card. With nothing focused it auto-anchors (draws that POI’s edges, same as v0.4); with an anchor it peeks — the card opens, the overlay stays.
getAssociations(poiId)Returns a promise for that POI’s associations payload ({ poiId, items }), served from the child’s cache. Rejects after 8 s, which is also what an older child that does not know the verb produces. Does not select or render anything.
clearSelection()Clears the selected POI.
on(type, handler)Subscribes to an SDK event.
off(type, handler)Unsubscribes a handler from an SDK event. Throws on an unknown event name, matching on.
isConnected()Returns whether the child has answered ready.
getContext()Returns the current context.
destroy()Removes the embed.
.frameReferences the generated iframe element.

Commands issued before the ready handshake are queued and replayed in order. setContext never changes frame.src, so it does not reload the iframe.

isConnected() does not prove that the embed understands the site-embed verbs; it only shows that the child answered ready. Listening for contextApplied after calling setContext is the only reliable capability check.

Associations are typed edges between POIs — parking, trailhead, insider tip, food nearby, historically connected. The map draws the edges of the anchor set (usually the one place a page is about) by decorating each target’s own pin — a ring in the category colour, a small badge with the edge type, and a label — with dotted connectors for access edges. A first tap on a decorated pin or on a row in the card highlights it: the pin pulses and the map fits the anchor and that target together (associationHighlighted). A second tap, or the row’s Details ›, peeks at that place: its card opens, the anchor’s edges stay exactly where they were, and the card offers Focus here (re-anchor, which pushes the previous anchor onto a stack) and Back. back() returns to the previous anchor from cache. Opening any card when nothing is focused auto-anchors, so a map-first visitor always sees edges on their first click.

const site = Wander.siteMap(el, { mapId, mode: 'emphasize', context: items });
site.on('focusChanged', ({ poiIds, depth, reason }) => renderCrumbs(poiIds, depth));
site.on('associationSelected', ({ toPoiId }) => showPeekBar(toPoiId));
site.focus(poiId, { fit: true });
site.getAssociations(poiId).then(({ items }) => renderStrip(items));
site.back();

Use emphasize rather than only on pages that show edges, so the rest of the destination stays visible while the visitor explores. A hub page can pass an array to focus to draw the edges of every place it lists.

Site-map events beyond the list embed’s: loading { namespace, mapId }, contextApplied, poiHovered, poiUnhovered, clusterClicked, focusChanged { poiIds, depth, reason: 'focus' | 'back' | 'clear' }, associationHighlighted { fromPoiId, toPoiId, type, source }, associationsLoaded { poiIds, poiId, count, byCategory }, associations { poiId, items }, associationSelected { fromPoiId, toPoiId, type }, and error { scope: 'associations', poiId }. The site map’s selection events are poiSelected { poiId, source: 'host' | 'user' } and poiDeselected { source }. host means select, focus, back, clearFocus, flyTo with select: true, clearSelection, setContext, or setMode caused the change; user means a tap or card button caused it. An older child omits source, so treat undefined as user. Each item in items is { id, association_type, is_navigational_target, display_label, sort_order, direction: 'forward' | 'reverse', associated_poi: { id, type, name, coordinates } }. See Site embed for the bridge-level tables.

Wander.siteEmbedUrl(options) returns the same URL for a server-rendered iframe, mirroring Wander.embedUrl(options).

A page opened from file:// has the opaque origin "null". The embed guards its acknowledgements accordingly, so local demo harnesses work.