Add a Geocoder Search Control to MapLibre GL JS
This example adds a search control to the top right corner of a MapLibre GL map using the maplibre-gl-geocoder plugin. Results are fetched from the Maptoolkit Geocoding API. Try searching for Sillgasse.
Dependencies: maplibre-gl-geocoder
let map = new maplibregl.Map({
container: "map",
style: "https://styles.maptoolkit.net/maptoolkit/maptoolkit.summer.json?api_key=YOUR_API_KEY",
center: [11.40037, 47.26816],
zoom: 12,
});
map.addControl(
new MaplibreGeocoder({
forwardGeocode: async (cfg) => {
const response = await fetch(
`https://geocoder.maptoolkit.net/search?q=${encodeURIComponent(cfg.query)}&language=${cfg.language[0]}&api_key=YOUR_API_KEY`
);
const result = await response.json();
return {
features: result.map((e) => ({
type: "Feature",
geometry: { type: "Point", coordinates: [e.lon, e.lat] },
place_type: ["place"],
place_name: e.display_name,
text: e.type,
properties: e,
center: [e.lon, e.lat]
}))
};
},
}, {
showResultsWhileTyping: true,
showResultMarkers: false,
maplibregl: maplibregl
})
);<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://cdn.jsdelivr.net/npm/maplibre-gl@5.6.1/dist/maplibre-gl.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/maplibre-gl@5.6.1/dist/maplibre-gl.css" />
<script src="https://cdn.jsdelivr.net/npm/@maplibre/maplibre-gl-geocoder@1.2.0/dist/maplibre-gl-geocoder.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/@maplibre/maplibre-gl-geocoder@1.2.0/dist/maplibre-gl-geocoder.css" rel="stylesheet" />
<style>
html, body { width: 100%; height: 100%; margin: 0; padding: 0; }
#map { width: 100%; height: 100%; }
</style>
</head>
<body>
<div id="map"></div>
<script>
let map = new maplibregl.Map({
container: "map",
style: "https://styles.maptoolkit.net/maptoolkit/maptoolkit.summer.json?api_key=YOUR_API_KEY",
center: [11.40037, 47.26816],
zoom: 12,
});
map.addControl(
new MaplibreGeocoder({
forwardGeocode: async (cfg) => {
const response = await fetch(
`https://geocoder.maptoolkit.net/search?q=${encodeURIComponent(cfg.query)}&language=${cfg.language[0]}&api_key=YOUR_API_KEY`
);
const result = await response.json();
return {
features: result.map((e) => ({
type: "Feature",
geometry: { type: "Point", coordinates: [e.lon, e.lat] },
place_type: ["place"],
place_name: e.display_name,
text: e.type,
properties: e,
center: [e.lon, e.lat]
}))
};
},
}, {
showResultsWhileTyping: true,
showResultMarkers: false,
maplibregl: maplibregl
})
);
</script>
</body>
</html>Use the prompt below with any LLM to get the same result. Make sure the Maptoolkit MCP server is connected first — check out AI Integration & MCP to get started.
How it works
maplibre-gl-geocoder supplies the search box and expects a forwardGeocode function, so
this example supplies one that calls Maptoolkit instead of the plugin’s default provider.
The response is Nominatim-shaped, which explains several details that
otherwise look arbitrary. lat and lon come back as strings, not numbers, so they need
coercing before a map will accept them. display_name is the full formatted address.
boundingbox is [south, north, west, east], also as strings, which is a different order
from the [west, south, east, north] that most GeoJSON tooling expects.
The query has to be URL-encoded. encodeURIComponent is not optional: an address with a
comma or an ampersand truncates the query without it.
The plugin expects GeoJSON Features, so each result is rebuilt into one, with center set
alongside geometry because the control reads that field when flying to a result.
showResultsWhileTyping: true queries the service on each keystroke. Debounce it if request
volume matters.
Next steps
Reverse geocoding is the other half, turning a coordinate back into an address, which is what a click-anywhere or drag-a-pin interface needs. The Geocoding API reference covers both directions and the parameters that narrow results by country or feature type.
Most searches are the first step of something else. The Routing API takes the coordinates a result returns, and the Isochrone API turns a found address into the area reachable from it.