Skip to main content

Attach the map editor

At the end of this guide your page shows Kaleidr's on-map editing tools over a Kaleidr basemap, themed to your brand. This works on every plan, Free included. You need a publishable key that carries the design scope, which new keys carry by default.

Edits are not saved

The embedded editor keeps its work in the page's memory. Nothing is saved, exported or published from the embed, and your page gets no callback when something changes. A reload, or handle.destroy(), discards the edits. If you need maps that persist, create them in Kaleidr Studio and embed them with Viewer.

Before you start​

  • A publishable key (kld_pk_live_…) from kaleidr.com/api-keys. See Get an API key.
  • The origin of the page you will test on, for example http://localhost:3000, in the key's allowed origins. A key with no allowed origins works on any site.
  • A way to serve the page over HTTP, for example npx serve ..
  • If your site sends a Content Security Policy, the editor directives from Content Security Policy. The editor runs in your page, so it needs connect-src, img-src, worker-src, font-src and style-src entries that the iframe products do not.
  • No other Kaleidr editor on the same page.

Step 1: Add a sized container and mount​

<div id="editor" style="height:640px"></div>

<script src="https://cdn.kaleidr.com/embed/v1/kaleidr.js"></script>
<script>
const handle = Kaleidr.mount('#editor', {
product: 'editor',
publishableKey: 'kld_pk_live_…',
});
</script>

What you should see: a Kaleidr basemap at world zoom filling the container (kaleidr-morning, unless your organization has set another default), with a Create button on the map. Choose Create to open the editor's sidebar with the Text, Image, Marker and Popup tools.

The container must have a height. The editor is 100% of the container's width and height, and its panels stay inside the container.

You can also use the element. It is display:block by default, so the height applies; the snippet writes display:block as well, for Safari before 16.4:

<kaleidr-map product="editor" publishable-key="kld_pk_live_…"
style="display:block; height:640px"></kaleidr-map>

Step 2: Choose the basemap and the opening camera​

const handle = Kaleidr.mount('#editor', {
product: 'editor',
publishableKey: 'kld_pk_live_…',
styleId: 'kaleidr-night',
center: [-0.1276, 51.5072], // [longitude, latitude]
zoom: 12,
pitch: 45,
bearing: -20,
});

What you should see: the dark basemap over central London, tilted and rotated.

How the basemap is chosen:

  1. styleId, if you pass one. Use a style id such as kaleidr-morning, kaleidr-afternoon, kaleidr-evening, kaleidr-night, kaleidr-plain or kaleidr-editorial. The list with descriptions is in the Tile reference.
  2. Otherwise your organization's default basemap, if one is set.
  3. Otherwise kaleidr-morning.

Camera defaults are center [0, 0], zoom 2, pitch 0 and bearing 0. Unlike Viewer and Tile, the editor accepts zoom without center.

A camera option passed to Kaleidr.mount that is not a finite number is ignored, and the console says so. On the element, a camera attribute that is not a number is dropped with no message.

[kaleidr:editor] ignored zoom: abc is not a finite value — using 2 instead.

Step 3: Theme the panels (optional)​

Pass theme to match the editor's panels to your product. It is an object with up to 17 named keys. Keys outside that list are ignored.

const handle = Kaleidr.mount('#editor', {
product: 'editor',
publishableKey: 'kld_pk_live_…',
theme: {
brandColor: '#0b5fff',
brandColorHover: '#0847c2',
brandColorOn: '#ffffff',
fontPrimary: '"Inter", system-ui, sans-serif',
cardRadius: '12px',
buttonRadius: '8px',
},
});

What you should see: the active tool, focus rings and primary buttons in your brand color, and the panels in your font.

All 17 keys are listed in the Editor reference.

Three things to know:

  • The values are CSS values. brandColor takes any CSS color, cardRadius any CSS length, fontPrimary any font-family value. Load the font yourself.
  • The theme is applied to the whole document while the editor is mounted and is restored when the editor is destroyed. It does not change your own styles unless they use the same custom properties.
  • The theme is read at mount only. handle.setTheme is not supported on the editor. To change the theme, destroy the editor and mount it again, which discards the edits.

On the element, theme is a JSON string:

<kaleidr-map product="editor" publishable-key="kld_pk_live_…"
theme='{"brandColor":"#0b5fff","buttonRadius":"8px"}'
style="display:block; height:640px"></kaleidr-map>

Step 4: Move the camera from your page (optional)​

document.getElementById('paris').addEventListener('click', () => {
// With a duration the map animates. Without one it jumps.
handle.setCamera({ center: [2.3522, 48.8566], zoom: 12, duration: 1000 });
});

What you should see: the map flies to Paris. Anything the person placed on the map stays where it was placed.

setCamera is the only way your page can act on the editor's map. The handle does not expose the map object.

Step 5: Tear down​

handle.destroy();

destroy() removes the editor, its map and its panels from the container and restores the theme values. The edits are discarded.

In a single-page app, call destroy() when the route or component that owns the editor goes away. If you use the element, removing it from the document does the same. See Framework integration.

Because only one editor can exist on a page at a time, always destroy the current editor before you mount another.

A complete page​

Save this as index.html, put your key in, and serve the folder over HTTP from an origin the key allows.

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Kaleidr editor</title>
<style>
body { font-family: system-ui, sans-serif; margin: 0; }
header { padding: 12px 16px; display: flex; gap: 8px; align-items: center; }
#editor { height: calc(100vh - 56px); }
</style>
</head>
<body>
<header>
<strong>Sketch on the map</strong>
<button id="london">London</button>
<button id="paris">Paris</button>
<button id="reset">Start over</button>
<span>Your work is not saved.</span>
</header>

<div id="editor"></div>

<script src="https://cdn.kaleidr.com/embed/v1/kaleidr.js"></script>
<script>
const options = {
product: 'editor',
publishableKey: 'kld_pk_live_…',
styleId: 'kaleidr-morning',
center: [-0.1276, 51.5072],
zoom: 11,
theme: { brandColor: '#0b5fff', brandColorOn: '#ffffff' },
};

let handle = Kaleidr.mount('#editor', options);

document.getElementById('london').addEventListener('click', () => {
handle.setCamera({ center: [-0.1276, 51.5072], zoom: 11, duration: 1000 });
});
document.getElementById('paris').addEventListener('click', () => {
handle.setCamera({ center: [2.3522, 48.8566], zoom: 11, duration: 1000 });
});

// One editor per page: destroy before mounting again.
document.getElementById('reset').addEventListener('click', () => {
handle.destroy();
handle = Kaleidr.mount('#editor', options);
});
</script>
</body>
</html>

How it behaves​

TopicBehavior
Where it runsIn your document. The map canvas and the editor's panels are elements inside the mount target.
SessionThe SDK exchanges your key for a short-lived session before the editor starts, and renews it while the editor is mounted.
EditsHeld in memory. Not saved, not exported, not published, not reported to your page.
BasemapLoaded by the page from https://tile.kaleidr.com. Fixed at mount.
ThemeApplied document-wide at mount, restored at destroy.
CameraSet at mount, moved with handle.setCamera.
UsageThe editor's session request does not count a map load, and the embedded editor has no AI features that spend credits. Its basemap requests that carry your key add map loads in bulk, one for every 20 requests in an hour: see Map loads.
SignalsNone. There is no ready event, no change event and no error callback.

Tell your users​

The editor itself does not warn that work is temporary. If people will spend time in it, say so in your own interface, as the complete page above does, so nobody expects to find their map again later.

Check that it worked​

  • On the page: the basemap fills the container, the Create button opens the sidebar, and you can place a text or a marker on the map.
  • In the console: no line that contains [kaleidr.
  • In the Network tab: kaleidr.js and editor.js from https://cdn.kaleidr.com; one POST …/sdk/sessions with status 200; requests to https://tile.kaleidr.com for the style and the map tiles.

If it does not work​

The SDK logs a failed mount to the console as [kaleidr:editor] …. For a refused session it logs only the HTTP status. The reason is in the response, which you can read in the Network tab by selecting the sessions request. A 403 carries its code in the detail field. A 429 carries its code in the error field, with a sentence in detail.

SymptomCauseFix
Nothing renders. Console: [kaleidr:editor] [editor-embed] attach() requires an { apiKey }No key was passed.Pass publishableKey.
Nothing renders. The console shows a CORS error for …/sdk/sessions and a [kaleidr:editor] fetch errorThe key is unknown or revoked, or the page's origin is not in the key's allowed origins.Check the key. Add the page's exact origin (scheme, host and port) at kaleidr.com/api-keys and allow a few minutes for the change to apply.
Console: [kaleidr:editor] kaleidr session exchange failed (403)The key does not carry the design scope (insufficient_scope), is limited to other products (product_not_allowed), or the plan does not admit the request (tier_capability_not_allowed).Read detail in the Network tab. Create a key with the default scopes.
Console: [kaleidr:editor] Kaleidr: a server key (kld_sk_…) must never be used in the browser…You passed a server key.Use a publishable key. Rotate the server key if the page was ever public.
The container stays empty and has no heightThe mount target has no height.Give it one. On the element in Safari before 16.4, add display:block too.
The panels render but the map is blank. Console: [kaleidr:editor] … errors. The Network tab shows 429 responses from https://tile.kaleidr.com with "error":"tile_access_restricted"The basemap host refused the page's requests.Confirm the key is a live publishable key and the page's origin is in its allowed origins, wait a few minutes after creating or editing the key, and reload. If it continues, contact support.
The panels render but the map is blank, and the console reports a Content Security Policy violationYour policy blocks the basemap, the map's worker, or its images.Add the editor directives from Content Security Policy.
The editor has no stylingYour policy blocks inline styles.Allow 'unsafe-inline' in style-src.
A second editor misbehaves, or the first stops workingTwo editors are mounted.Destroy the first before mounting the second.
[kaleidr] setTheme() is not supported on product "editor" — the call is a no-op.The theme is mount-time only.Pass theme at mount.
[kaleidr] theme string must be JSON…The theme attribute is not a JSON object.Use single quotes around the attribute and double quotes inside the JSON.
A theme key has no effectThe key is not one of the 17, or is misspelled. Unknown keys are ignored without a message.Check the key against the list.
The edits disappearedThe page reloaded, or the editor was destroyed or its element was moved or removed.Expected. See the note at the top of this page.

Limits​

  • Nothing is saved, exported or published from the embed.
  • No change callback, no ready signal, no error callback.
  • No access to the map object, so no custom layers and no way to read what was drawn.
  • No Data tab, design catalog, AI panel, 3D model library, sharing or projects. The rail has Text, Image, Marker, Popup, Background, Layer, Design and Legend; see Scope.
  • One editor per page.
  • The basemap and theme cannot change after mount.

Next steps​