Viewer reference
product="viewer" shows a map published in Kaleidr Studio. The SDK replaces
the contents of the mount target with an iframe that loads
https://map-embed.kaleidr.com/embed/<id>. The map draws inside that iframe.
| Key | None. Do not pass one. |
| Plan | Every plan, Free included. |
| Renders in | An iframe from https://map-embed.kaleidr.com. |
| Metering | One map load per view, charged to the map owner's organization. |
| CSP | script-src https://cdn.kaleidr.com and frame-src https://map-embed.kaleidr.com. |
For a walkthrough, see Embed a published map.
Mount
With the element:
<script src="https://cdn.kaleidr.com/embed/v1/kaleidr.js"></script>
<kaleidr-map product="viewer" share-id="YOUR_MAP_ID"
style="display:block; height:480px"></kaleidr-map>
From JavaScript:
const handle = Kaleidr.mount('#map', {
product: 'viewer',
shareId: 'YOUR_MAP_ID',
});
With no SDK at all:
<iframe src="https://map-embed.kaleidr.com/embed/YOUR_MAP_ID"
width="100%" height="480" style="border:0;" loading="lazy"
referrerpolicy="no-referrer-when-downgrade" allow="geolocation; microphone; fullscreen; clipboard-write"
title="Kaleidr map"></iframe>
Chat bar
layout="bar" turns the viewer into a chat bar: one question field for your
page, with no map until a visitor asks. A question opens the published map, and
the map's Kaleidr AI answers it there.
<script src="https://cdn.kaleidr.com/embed/v1/kaleidr.js"></script>
<kaleidr-map product="viewer" share-id="YOUR_MAP_ID" layout="bar"
chat-title="Ask about the neighborhood"
prompts="Coffee within a short walk|Parks nearby"></kaleidr-map>
Answers open in one of two places:
| Setting | What a question does |
|---|---|
No results-url (the default) | The map opens over the page, filling the window, and asks the question. The visitor closes it with its close button. A later question asks the same map, so the conversation continues. |
results-url="/map" | The browser goes to that page with ?q=<question>. A viewer there with prompt-param="q" asks the question when the map opens, if the visitor came from a page of the same origin. |
A results page:
<script src="https://cdn.kaleidr.com/embed/v1/kaleidr.js"></script>
<kaleidr-map product="viewer" share-id="YOUR_MAP_ID" prompt-param="q"
style="display:block; height:600px"></kaleidr-map>
- The map answers. The question goes to the map's own Ask Kaleidr panel, so the map needs Control Tower on (Studio, Kaleidr AI), and the rules of Kaleidr AI on a published map apply: the owner pays, and the map's allowed domains decide where it answers. On a map without Control Tower, the map opens and the question is not asked.
- The bar loads no map. The map loads, and counts as a map load, when a visitor asks.
- The bar draws in a shadow root of its own, so your page's CSS does not
restyle it, and its styles need no
style-srcpermission. It is as wide as the element and as tall as its content. - Its own labels follow
lang, or the visitor's browser language whenlangis not set: English, Spanish, French, German, Portuguese, Japanese, Korean, simplified and traditional Chinese, and Arabic. Yourchat-title,placeholderandpromptsare shown as you write them. promptpre-fills the field, andhandle.send()asks from code.handle.send('Parks nearby')asks that question;handle.send()asks what is in the field.
The builder at template.kaleidr.com writes both snippets for a map you choose.
Options
| Mount option | Attribute | Type | Default | Required | Description |
|---|---|---|---|---|---|
product | product | 'viewer' | none | yes | Selects the viewer. |
shareId | share-id | string | none | yes | The id of the published map, from Studio's embed code. Must match ^[A-Za-z0-9._~-]{1,100}$. |
mapId | map-id | string | none | no | Alias of shareId, used only when shareId is absent. |
center | center | [longitude, latitude]; the attribute is "longitude,latitude" | the saved view | no | Opening center. |
zoom | zoom | number | the saved view | no | Opening zoom. Ignored at mount unless center is also given. |
pitch | pitch | number, degrees | the saved view | no | Opening tilt. |
bearing | bearing | number, degrees | the saved view | no | Opening rotation. |
viewerBase | viewer-base | URL | https://map-embed.kaleidr.com | no | Origin the iframe loads from. Only the origin is used. You do not need this in production. |
base | base | URL | https://cdn.kaleidr.com | no | Origin the SDK loads viewer.js from. Only the origin is used, and only the origins listed under The base override are honored. |
enabled | flag | boolean | none | no | Per-mount arm of the enable gate. |
prompt | prompt | string | none | no | A question to put to the map's Kaleidr AI when the map opens. On a chat bar, the field's pre-filled text. At most 500 characters. |
promptParam | prompt-param | string | none | no | Reads prompt from this query parameter of the page's own URL: q on a chat bar's results page. Read only when the visitor came from a page of the same origin, so a link from elsewhere cannot make your map ask anything. |
layout | layout | 'map' or 'bar' | 'map' | no | bar mounts the chat bar. |
prompts | prompts | array of strings; the attribute separates them with | | none | no | Chat bar: example questions, shown as buttons that ask them. |
title | chat-title | string | none | no | Chat bar: the label above the field. |
placeholder | placeholder | string | "Ask about this map", in the bar's language | no | Chat bar: the field's placeholder. |
accent | accent | CSS color | #2da84a | no | Chat bar: the color of the send button, the icon and the focus ring. |
theme | theme | 'light', 'dark' or 'auto' | 'light' | no | Chat bar: its colors. auto follows the visitor's system setting. |
lang | lang | language tag | the visitor's browser language | no | Chat bar: the language of its own labels. |
resultsUrl | results-url | URL | none | no | Chat bar: sends visitors to this page with ?q=<question> instead of opening the map over the page. |
publishableKey, styleId, map, mapTarget and contained have no
effect on a viewer mount. A key is worse than useless here: the SDK
exchanges it for a session the viewer never uses, and if that exchange is
refused the map does not render.
The id
shareId accepts either kind of id that Studio produces for a map:
- The published map's id. It is the id in the snippet on the Publish & embed page and in the Share dialog's Embed tab; the two show the same id.
- A share link's id. It is the 16-character id at the end of the URL on the Share dialog's Link Share tab.
The published map's id shows the map as it is currently saved. A share link's id shows the snapshot taken when that link was created. Kaleidr AI accepts either id: on a share link it follows the snapshot's Kaleidr AI switches, while the map is published and on the map's current allowed domains; see Chat on a published map.
An empty or malformed id does not throw. The SDK logs
[kaleidr:viewer] Kaleidr map: invalid id "" — must match ^[A-Za-z0-9._~-]{1,100}$.
and nothing renders. Kaleidr.mount still returns a handle, whose methods do
nothing.
Plain iframe URL
https://map-embed.kaleidr.com/embed/<id>?camera=<lon>,<lat>[,<zoom>]&pitch=<deg>&bearing=<deg>&q=<question>
| Query parameter | Value | Effect |
|---|---|---|
camera | longitude,latitude or longitude,latitude,zoom | Opening center and, optionally, zoom. |
pitch | number, degrees | Opening tilt. |
bearing | number, degrees | Opening rotation. |
q | text, at most 500 characters | A question to put to the map's Kaleidr AI when the map opens. Asked only when the viewer is embedded in a page, and ignored on a map without Control Tower. |
The SDK builds the same URL from its camera options and prompt.
Camera
The opening camera is assembled from these sources. A later source replaces only the fields it provides.
- The basemap's default view.
- The view the author saved in Studio. An author can save a separate view for desktop, tablet and phone widths, in which case the map uses the one that matches the width of the frame. An author can instead set the map to frame its content, in which case the map fits that content to the frame.
- The camera you pass (
center,zoom,pitch,bearingas options or attributes, orcamera,pitchandbearingin a plain iframe URL).
A camera your page passes wins over the author's view, including a view saved for the visitor's screen width and content framing. The fields you do not pass keep the author's values.
A map with per-width views or content framing frames itself again when the
frame is resized, until the visitor moves the map. A camera from your page
ends that too: after a camera passed at load, or a setCamera call, the map
no longer re-frames itself on resize. setCamera changes only the fields
you give it and leaves the others as they are.
center is longitude first: [-0.1276, 51.5072] is London.
Sizing
- The iframe is 100% of the mount target's width, has no border, and has rounded corners.
- Its height is 100% of the target when the target has a height at mount time. Otherwise it is a fixed 500 pixels.
- The mount replaces the target's children and sets
position:relative(if the target has no inline position) andoverflow:hiddenon it. <kaleidr-map>isdisplay:blockby default, so aheighton it applies. Give it a height. The snippets also writedisplay:block, which covers Safari before 16.4, where the element stays inline.
Handle
Kaleidr.mount returns the handle synchronously. See the
handle matrix for every product.
| Member | On viewer |
|---|---|
handle.product | 'viewer' |
handle.setCamera(camera) | Moves the map. |
handle.setTheme(theme) | Not supported. Logs a warning and does nothing. |
handle.setStyle(style) | Not supported. Logs a warning and does nothing. |
handle.send(text) | Asks the map's Kaleidr AI, as if the visitor typed text. On a chat bar, send() with no text asks what is in the field. A question sent while one is being answered waits for it, and a newer one replaces any still waiting. |
handle.destroy() | Removes the iframe and stops listening. |
setCamera(camera)
handle.setCamera({
center: [2.3522, 48.8566], // [longitude, latitude]
zoom: 12,
pitch: 45,
bearing: -20,
duration: 800, // milliseconds
});
| Field | Type | Description |
|---|---|---|
center | [number, number] | Longitude, latitude. |
zoom | number | Zoom level. Unlike at mount, zoom works without center here. |
pitch | number | Tilt in degrees. |
bearing | number | Rotation in degrees. |
duration | number | Animation length in milliseconds. Omitted or 0 jumps with no animation. |
All fields are optional. Fields you leave out keep their current value.
You can call setCamera at any time after Kaleidr.mount returns, including
on the next line. A call made before the map inside the iframe is ready is
kept and applied, in order, when the map reports ready. The latest 256
calls are kept. If the map has not reported ready after a bounded wait,
normally 10 seconds after the frame loads, the kept calls are sent anyway,
with one console warning. You do not need to
wait for the kaleidr-embed:ready message first. For the view the map opens
on, mount options are still the direct way: the map starts there instead of
moving there.
There is no method that reads the camera back.
Element attributes after mount
On a mounted <kaleidr-map product="viewer">, changing center, zoom,
pitch or bearing moves the map with no animation. Changing share-id or
any other attribute does nothing. To show a different map, remove the element
and insert a new one.
Messages from the iframe
The iframe posts messages to the embedding page. Two of them are the public signal for "loaded" and "failed":
// The map drew for the first time.
{ type: 'kaleidr-embed:ready', id: string, source: 'publish' | 'share' }
// The map could not load, or part of it failed to draw.
{ type: 'kaleidr-embed:error', id: string | null, code: string, message: string }
window.addEventListener('message', (event) => {
if (event.origin !== 'https://map-embed.kaleidr.com') return;
if (event.source !== iframe.contentWindow) return; // which embed sent it
const msg = event.data;
if (msg?.type === 'kaleidr-embed:ready') onReady(msg);
if (msg?.type === 'kaleidr-embed:error') onError(msg);
});
With the SDK, iframe is the first <iframe> inside the mount target:
target.querySelector('iframe').
Error codes
code | Terminal | Meaning |
|---|---|---|
no_id | yes | The iframe URL carries no map id. id is null. |
not_found | yes | No published map or share link has this id: the map was never published or was unpublished, the share link's map was deleted or Kaleidr removed the link, or the id is mistyped. |
domain_not_allowed | yes | The map has an allowed-domain list and the embedding page's domain is not on it, or the page sent no referrer. |
map_loads_exhausted | yes | The owner's organization has used its map loads for the period. |
fetch_failed | yes | The map could not be fetched. |
maplibre_error | no | The map started, but something failed to draw. May arrive more than once, before or after ready. |
After a terminal code, the frame shows a short message in place of the map and
ready is not sent.
Rules
readyis sent once per load, after the map has drawn and the view has been counted.- The SDK creates the iframe with
loading="lazy", so neither message arrives until the frame comes near the viewport. - The frame posts other messages whose
typestarts withkaleidr-embed:. The SDK uses them to connect a chat panel to the map. They are not a public contract and can change. Do not build on them. - The frame acts on control messages, such as the camera move behind
setCamera, only when they come from the page that directly embeds it. A message posted to the frame from any other window or frame is ignored, so callsetCamerafrom the page that holds the iframe. - The SDK and the element emit no DOM events and offer no
readypromise.
Messages the visitor can see
These appear inside the frame, in place of the map:
| Text | Code |
|---|---|
| "Loading map…" | none: the map is loading |
| "Couldn't find a map id in the URL." | no_id |
| "This map isn't available — it may have been unpublished, paused, or the link is incorrect." | not_found |
| "This map isn't allowed to render on this site. Please contact the map owner." | domain_not_allowed |
| "This map has reached its map load limit. It will be available again when the owner's allowance resets." | map_loads_exhausted |
| "Could not load the map. Please try again in a moment." | fetch_failed |
| "Unexpected error loading the map." | none: an unexpected failure during startup |
Uploaded data
A published map draws the datasets and image overlays its author added in Studio's Data panel. The frame loads them itself; no option, attribute or key is involved, and the data never passes through your page.
| Behavior | Detail |
|---|---|
| What is shown | Every dataset and image overlay the published map includes, as the layer its author styled. |
| Who can read it | Anyone who can open the map. Allowed domains do not make the data confidential. |
| What a visitor's browser receives | Each dataset whole, with all of its columns. A large dataset takes longer to appear. |
| A dataset that cannot be loaded | The rest of the map draws, without that layer. |
| Deleting a dataset in Studio's Data panel | The dataset is deleted and no longer served, on the published map and on every share link that drew it. A browser that already loaded it keeps it, and a download link already issued can stay readable for up to one hour. |
| Unpublishing the map | Nothing of the map is served through its published id, its datasets included. The frame shows the not_found message. Publishing again restores the same id. |
| Share links | A share link is a snapshot of the map's design when the link was created. It keeps showing its map after the map is unpublished, without Kaleidr AI until the map is published again. Studio has no control to delete or pause one: it keeps working until its map is deleted or Kaleidr removes the link at the owner's request. See Share links. |
Uploaded data on a published map covers each of these.
Allowed domains
The map's owner can limit the sites a map renders on, in Studio under Share, Embed, Specific domains.
- A listed domain covers its subdomains.
- The check compares the list with the host name of the referrer the browser sends for the iframe. A missing referrer fails the check.
- A failed check shows the "isn't allowed" message and posts
domain_not_allowed. - A view that fails the check is not counted as a map load.
Metering
- A view counts one map load against the organization that owns the map.
- The same visitor viewing the same map again within 24 hours is not counted again.
- Views on Kaleidr's own sites are not counted.
- When the allowance is used up, the frame shows the limit message until the allowance resets. See Quota and rate limits.
Console messages
| Message | Meaning |
|---|---|
[kaleidr] <kaleidr-map> requires product="chat|viewer|editor|tile" | The element has no valid product. Nothing mounts. |
[kaleidr:viewer] Kaleidr map: invalid id "…" — must match ^[A-Za-z0-9._~-]{1,100}$. | The id is empty or malformed. |
[kaleidr:viewer] mount target not found: <selector> | Kaleidr.mount found no element for the selector. |
[kaleidr:viewer] [kaleidr] failed to load https://cdn.kaleidr.com/embed/v1/viewer.js | The viewer bundle could not load: a network failure, an ad blocker, or a script-src policy. |
[kaleidr] setStyle() is not supported on product "viewer" — the call is a no-op. … | You called a handle method the viewer does not have. |
Not available
- Events for clicks, camera moves or selections.
- Reading the camera, the layers or the data of the map.
- Adding your own markers or layers to the embedded map.
- Changing the basemap or theme from the embed.