Private Plugins¶
This notebook demonstrates how to dynamically load private or third-party MapLibre plugins at runtime using add_plugin().
add_plugin() supports three loading strategies:
| Strategy | When to use |
|---|---|
js_url (classic script) |
Plugin exposes a global UMD/IIFE init function |
js_url + module=True |
Plugin is an ESM package on a CDN (e.g. esm.sh) that exports an init function |
js_code |
Complex plugin needing custom setup logic; write a small wrapper module inline |
The example below uses maplibre-gl-geo-editor to show how you would load a private NPM package that is not bundled with anymap-ts.
# %pip install anymap-ts
Load a Plugin via Inline JS Code (js_code)¶
The most flexible approach for complex plugins is js_code. You write a small
ESM wrapper module as a Python string. The browser executes it as a real ES module
(via a Blob URL — no eval), so import statements work normally.
geo-editor requires two packages:
@geoman-io/maplibre-geoman-free– the underlying geometry editing enginemaplibre-gl-geo-editor– the MapLibre control wrapper
The wrapper below imports both, initialises Geoman, wires up the GeoEditor
control, and returns a destroy() hook so remove_plugin() cleans up properly.
from anymap_ts import Map
# CSS for both packages (loaded from esm.sh)
GEO_EDITOR_CSS_URL = (
"https://esm.sh/@geoman-io/maplibre-geoman-free/dist/maplibre-geoman.css"
)
# Inline ESM wrapper — acts as the "private plugin module"
GEO_EDITOR_JS_CODE = """
import { Geoman } from 'https://esm.sh/@geoman-io/maplibre-geoman-free';
import { GeoEditor } from 'https://esm.sh/maplibre-gl-geo-editor';
export async function init(map, config) {
const position = config.position ?? 'top-right';
// 1. Add Geoman as a map source/layer engine
const geoman = new Geoman(map, {});
await geoman.onLoad();
// 2. Create and add the GeoEditor control
const editor = new GeoEditor({ geoman });
map.addControl(editor, position);
// 3. Return an object with a destroy() method for clean teardown
return {
editor,
geoman,
destroy() {
map.removeControl(editor);
geoman.remove?.();
},
};
}
"""
m = Map(center=[0, 20], zoom=2)
m.add_plugin(
name="geo-editor",
js_code=GEO_EDITOR_JS_CODE,
css_url=GEO_EDITOR_CSS_URL,
init_function="init",
config={"position": "top-right"},
)
m
Remove the Plugin¶
Call remove_plugin() to cleanly tear down the plugin. This calls the
destroy() method returned by init, removes injected <script> and
<style> elements, and frees the instance.
m.remove_plugin("geo-editor")
Load a Plugin from a URL (ESM module)¶
If your private plugin is already hosted (e.g. on an internal CDN or a
private npm registry proxy) and exports a single init(map, config) function,
use js_url with module=True. No wrapper code is needed.
m2 = Map(center=[0, 20], zoom=2)
m2.add_plugin(
name="my-esm-plugin",
# Replace with your private plugin URL, e.g.:
# js_url="https://cdn.example.com/private/maplibre-my-plugin.js",
js_url="https://esm.sh/my-maplibre-plugin@1.0.0", # placeholder
css_url="https://cdn.example.com/private/maplibre-my-plugin.css", # optional
module=True,
init_function="init", # or "default"
config={"position": "top-left"},
)
# m2 # uncomment to display the map
Load a Plugin from a URL (classic script)¶
For older UMD/IIFE bundles that attach themselves to window, use js_url
without module=True and set init_function to the global path
(dot-separated) that the script exposes.
m3 = Map(center=[0, 20], zoom=2)
m3.add_plugin(
name="my-umd-plugin",
# The script sets window.MyPlugin.init = function(map, config) { ... }
js_url="https://cdn.example.com/private/maplibre-my-plugin.umd.js",
css_url="https://cdn.example.com/private/maplibre-my-plugin.css",
module=False,
init_function="MyPlugin.init",
config={"position": "bottom-right", "theme": "dark"},
)
# m3 # uncomment to display the map
Plugin API Reference¶
Writing a compatible init function¶
// my-private-plugin.js (hosted on your internal CDN or served locally)
export function init(map, config) {
// config is the dict you pass as config= in Python
const ctrl = new MyControl(config);
map.addControl(ctrl, config.position ?? 'top-right');
// Optional: return an object with methods callable via call_plugin_method()
// and a destroy() hook called by remove_plugin().
return {
setOption(key, value) { ctrl.setOption(key, value); },
destroy() { map.removeControl(ctrl); },
};
}
Calling methods on a running plugin¶
m.call_plugin_method("geo-editor", "setOption", "theme", "dark")
add_plugin() parameters¶
| Parameter | Type | Description |
|---|---|---|
name |
str |
Unique identifier for the plugin |
js_url |
str |
URL to JS file (mutually exclusive with js_code) |
js_code |
str |
Inline ESM module code as a Python string |
css_url |
str |
URL to CSS file (optional) |
css_code |
str |
Inline CSS as a Python string (optional) |
module |
bool |
Load js_url as ESM via import() (default False) |
init_function |
str |
Export/global name of the init function |
config |
dict |
Passed as second argument to the init function |