This MapLibre GL JS plugin adds support for the Stadia Maps Search Autocomplete APIs to any MapLibre GL JS map.
Based on the Stadia Maps TS SDK, it automatically handles best-practice functionality for search, including debouncing of requests, caching of previous results, and zooming the map when a result is selected.
Adding the search box to a map is straightforward:
- Add the TS/JS and CSS dependencies.
- Instantiate the control (optionally customize the settings).
- Add the control to the map.
You can try everything out locally in a web browser without any special setup! To deploy to an internet server though, you'll need a Stadia Maps account. Learn more about how we authenticate requests in our documentation, and sign up for a free account (no credit card required) using our client dashboard.
First, add @stadiamaps/maplibre-search-box as a dependency of your project using your favorite package manager
like npm or yarn.
$ npm install --save @stadiamaps/maplibre-search-boxImport and use the package along with the map. (Be sure to add the control to the map!)
import { MapLibreSearchControl } from "@stadiamaps/maplibre-search-box";
import "@stadiamaps/maplibre-search-box/dist/maplibre-search-box.css";
const control = new MapLibreSearchControl();
const map = new maplibregl.Map({
container: "map",
style: "https://tiles.stadiamaps.com/styles/alidade_smooth.json", // stylesheet location
center: [-74.5, 40], // starting position [lng, lat]
zoom: 2, // starting zoom
});
map.addControl(control, "top-left");Add this to your <head>:
<script
src="https://unpkg.com/browse/@stadiamaps/maplibre-search-box/dist/maplibre-search-box.umd.js"
type="application/javascript"
></script>
<link
href="https://unpkg.com/@stadiamaps/maplibre-search-box/dist/maplibre-search-box.css"
rel="stylesheet"
/>Add this to your <body>:
<div id="map" style="height: 100vh; width: 100vw"></div>
<script type="application/javascript">
var control = new maplibreSearchBox.MapLibreSearchControl({
useMapFocusPoint: true,
});
var map = new maplibregl.Map({
container: "map",
style: "https://tiles.stadiamaps.com/styles/alidade_smooth.json", // stylesheet location
center: [-74.5, 40], // starting position [lng, lat]
zoom: 2, // starting zoom
});
map.addControl(control, "top-left");
</script>The MapLibreSearchControl constructor takes a set of options (as an object):
export class MapLibreSearchControlOptions {
useMapFocusPoint = true;
mapFocusPointMinZoom = 5;
fixedFocusPoint: [number, number] = null;
searchOnEnter = true;
hideResultsOnBlur = false;
maxResults = 5;
minInputLength = 3;
minWaitPeriodMs = 100;
layers: LayerId[] = null;
onResultSelected?: (feature: FeaturePropertiesV2) => void;
onResults?: (results: {
query: string;
features: FeaturePropertiesV2[];
}) => void;
baseUrl: string | null = null;
lang: string | null = null;
placeholder: string | null = null;
noResults: string | null = null;
animationOptions: AnimationOptions | null = null;
}If set, the map center point is used to influence the search for better contextual results.
On low zooms, the focus point is often unuseful for contextual results (e.g., on zoom 0, the whole world is visible, so the center is not valuable). This controls at what zoom the center is used to influence results.
A single point used to influence results (doesn't follow the map viewport).
For address-based forward geocoding applications, it often makes sense to use the forward geocoding (/search) endpoint
rather than autocomplete search, once you know that the user has completed their input.
The full forward geocoding endpoint is able to interpolate addresses,
and may provide better results with complete input than the autocomplete
endpoint. Opting in to this behavior will send a final forward geocoding request if the user presses enter.
If set, the results list is hidden whenever the search input loses focus (for example, when the user clicks the map or tabs away), and shown again when the input is refocused. The results aren't discarded, so refocusing brings back the same list without another API request. Selecting a result still works as usual: pressing one keeps focus on the input long enough for the selection to register.
Defaults to false, which leaves the results visible until the user selects one or clears the input.
new MapLibreSearchControl({
hideResultsOnBlur: true,
});Maximum number of results to return.
Minimum number of characters to wait for before making the first search.
The minimum time to wait between searches. Higher values decrease the number of API requests made, but increase the received latency of the input.
Which layers to use in the search. Defaults to all layers. See the Layers documentation for more details.
Note: if you want the fastest possible search, but don't mind excluding addresses or Point of Interest (venue)
results, use ['coarse'] for the best performance.
A callback to be invoked whenever a result is selected by the user. This is invoked with a single argument, the
GeocodingFeature for the result. This allows you take an action (such as autofilling your own form).
A callback to be invoked whenever a search request completes, with the query string and the resulting features (which may be an empty array). This is useful for tracking analytics (e.g., search text and result count) without needing to duplicate the underlying search request. It is not invoked if the request fails.
An optional override to the base API URL. This defaults to the primary Stadia Maps API endpoint. If you want
to use our EU endpoints to ensure traffic is handled by EU servers,
set the baseUrl to https://api-eu.stadiamaps.com.
An optional BCP47 language tag (e.g. en, de, fr) used to
request results localized in that language. When unset, the API's default language behavior is used.
Note that this localizes the search results only. The widget's own text is English by default; supply your own
translations via the placeholder and noResults options below.
An optional override for the search input's placeholder text. Use it to match your own product voice, or to pass in an
already-translated string from your application's own i18n system — the widget renders whatever you give it and does
not translate anything itself. Defaults to "Search for places...".
An optional override for the message shown when a search returns nothing. Same deal as placeholder. Defaults to
"No Results Found".
Controls how the map camera moves when a result is selected. This is MapLibre's own
AnimationOptions and is passed
through to the underlying camera call, so you can shorten the transition, supply your own easing function, or skip
the animation entirely. When unset, MapLibre's defaults apply (a flyTo, or a fitBounds for results with a
bounding box).
To move the camera faster without giving up the animation:
new MapLibreSearchControl({
animationOptions: { duration: 300 },
});To move instantly, which is worth doing if you depend on the map's final bounds right away, or want to avoid loading all the tiles the camera passes over:
new MapLibreSearchControl({
animationOptions: { animate: false },
});With animate: false, results are placed with jumpTo rather than flyTo, and results with a bounding box use
fitBounds's linear (instant) path.
dev- starts dev serverbuild- generates the following bundles: CommonJS (.cjs) ESM (.mjs) and IIFE (.iife.js). The name of bundle is automatically taken frompackage.jsonname propertytest- starts vitest and runs all teststest:coverage- starts vitest and run all tests with code coverage reportlint:scripts- lint.tsfiles with eslintlint:styles- lint.cssand.scssfiles with stylelintformat:scripts- format.ts,.htmland.jsonfiles with prettierformat:styles- format.cssand.scssfiles with stylelintformat- format all with prettier and stylelint
Formatting is split by file type: prettier owns scripts and markup, stylelint owns stylesheets. Prettier is configured
(via .prettierignore) to skip .css and .scss entirely, because the two tools disagree about SCSS — notably on
quote style, where prettier's preference fails stylelint's sass-guidelines preset and therefore CI. If you're changing
stylesheet formatting, stylelint --fix is the tool to reach for.
prepare- script for setting up husky pre-commit hookuninstall-husky- script for removing husky from repository