Embed a map with widget.js
A div with your map token and one script - that is all it takes. This page explains exactly what the widget does and how to use it in plugins, page builders and single-page apps.
The shortest code
<div class="msl-map" data-token="YOUR-TOKEN"></div>
<script src="https://app.my-store-locator.com/widget.js" async></script>The script finds every container and puts an iframe with the public map into it. You find the token in the app in Embed Studio or under Maps. It belongs to the map for good: when you change the template, colours or locations, the code stays the same.
The token is public - it is part of every embed code. It only allows showing a published map. Drafts are not reachable; the map then shows "Map not available".
Containers
The widget recognises three forms:
| Container | Note |
|---|---|
<div class="msl-map" data-token="…"> | recommended, any number per page |
<div id="mystorelocator" data-token="…"> | older form, still works (an ID only once per page) |
<div data-mystorelocator data-token="…"> | as an attribute, e.g. when the class is already taken in your system |
In plugins we recommend class="msl-map" plus your own unique ID, so you can find the container again yourself.
Attributes
| Attribute | Values | Meaning |
|---|---|---|
data-token | map token | required |
data-height | CSS height or auto | default 640px. With auto the iframe grows with the map content and the page scrolls instead of the iframe |
data-lang | de, en | language of the map and of the consent placeholder |
data-consent | true | consent mode: a placeholder first, the map loads after a click |
data-lazy | true | the iframe loads only when it comes close to the visible area |
data-offset | number in pixels | only with data-height="auto": height of a sticky header on your website, so the map does not slide under it |
data-layout | split | only with data-height="auto" and the finder template: list and map in two iframes, the map stays in place while scrolling |
Colour, font, corners, map style and density are set in the app. If one embed should look different from the map itself, use the iframe with URL parameters.
Several maps on one page
<div class="msl-map" data-token="TOKEN-STORES" data-height="480px"></div>
<div class="msl-map" data-token="TOKEN-DEALERS" data-lang="de"></div>
<script src="https://app.my-store-locator.com/widget.js" async></script>Include the script once. If it ends up on the page twice - for example because two plugin blocks each bring it along - you still get no duplicate maps: the second copy only looks for new containers.
Page builders and single-page apps
The widget watches the page. Containers inserted later (Elementor, Shopify theme editor, Shopware Shopping Experiences, React, Vue) get their map automatically. If the data-token of a container changes, the widget rebuilds the map with the new token.
When you want to control it yourself, use window.MyStoreLocator:
| Function | Effect |
|---|---|
MyStoreLocator.mount(el) | puts the map into exactly this container, even without class or ID. A finished container with a changed data-token is rebuilt |
MyStoreLocator.mountAll(root) | finds all containers below root (without an argument: in the whole document) |
// Example: create a container with JavaScript
const el = document.createElement('div');
el.dataset.token = 'YOUR-TOKEN';
el.dataset.lang = 'en';
document.querySelector('#locations').appendChild(el);
if (window.MyStoreLocator) window.MyStoreLocator.mount(el);window.MyStoreLocator exists as soon as widget.js has loaded. A container with class="msl-map" does not need the call - the widget finds it by itself.
Consent mode
With data-consent="true" the widget first shows a placeholder with a short privacy note and a Load map button instead of the map. The iframe loads only after the click.
The placeholder language follows data-lang. Without data-lang, the lang attribute of your page decides: de, de-DE, de-AT and so on give German, everything else English.
<div class="msl-map" data-token="YOUR-TOKEN" data-lang="en" data-consent="true" data-lazy="true"></div>
<script src="https://app.my-store-locator.com/widget.js" async></script>Note: In consent mode the map loads only after the click, but the widget.js script itself is loaded from app.my-store-locator.com when the page opens. If nothing at all should load from our server before consent, load the script after the click - as in the next example - or through your consent tool.
<div class="msl-map" data-token="YOUR-TOKEN" data-lang="en"></div>
<button type="button" id="msl-load">Load map</button>
<script>
document.getElementById('msl-load').addEventListener('click', function () {
var s = document.createElement('script');
s.src = 'https://app.my-store-locator.com/widget.js';
document.head.appendChild(s);
this.remove();
});
</script>Whether and which solution you need depends on your website and your consent setup. Background in the guide store locator and GDPR.
iframe instead of the script
Where scripts are not allowed, the iframe works on its own:
<iframe src="https://app.my-store-locator.com/#/m/YOUR-TOKEN?lang=en" title="Store map" style="width:100%;height:640px;border:0;max-width:100%" loading="lazy" allow="geolocation"></iframe>allow="geolocation" enables "My location"; the browser asks visitors first. For automatic height and the consent placeholder, use the widget.
Only on allowed websites
In Embed Studio you can switch on Show only on allowed websites per map (default: off). The map then shows "Map not available" inside iframes on other websites. The check runs in the visitor's browser. It makes embedding on other websites harder, but it does not make the public map data secret, and the direct link to the map keeps working. Enter the domains under Allowed websites: company.com also covers www.company.com, *.company.com covers all subdomains.
Content Security Policy
If your website sends a CSP, allow:
script-src https://app.my-store-locator.com;
frame-src https://app.my-store-locator.com;Reading the token from user input
If your plugin has an input field for the map, accept everything users might paste: the plain token, the full embed code or the map address (…/#/m/YOUR-TOKEN). A valid token matches ^[A-Za-z0-9_-]{6,64}$.
function mslToken(input) {
const s = String(input || '').trim();
const m = s.match(/#\/m\/([A-Za-z0-9_-]{6,64})/) || s.match(/data-token="([A-Za-z0-9_-]{6,64})"/);
const t = m ? m[1] : s;
return /^[A-Za-z0-9_-]{6,64}$/.test(t) ? t : null;
}The connect flow is more convenient for your users: they pick the map in the app and your plugin receives the token automatically.
Try it with your own locations.
Create an account, import your locations, embed the map. The Free plan stays free - and you can test Pro for 14 days.