# leaflet-geosearch turns any geocoding API into one uniform result shape

> leaflet-geosearch is a TypeScript library that adds address lookup to a Leaflet map, with fourteen provider adapters behind a single search method that always returns the same result object. It also works without Leaflet at all and has no dependencies, and its provider model is the part that makes it more than a search box widget.

**smeijer/leaflet-geosearch** — A geocoding/address-lookup library supporting various api providers.

- Repository: https://github.com/smeijer/leaflet-geosearch
- Website: https://leaflet-geosearch.meijer.works
- Stars: 1,133 · Forks: 281
- Language: TypeScript
- License: MIT
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/smeijer-leaflet-geosearch

## Address lookup as a control, and as a plain function

Leaflet.GeoSearch adds support for geocoding, address lookup, to a web application, and it comes with controls that can be embedded in a Leaflet map. The library is written with the latest browser technologies in mind, and it is worth noticing the sentence that follows: although the project is still named leaflet-geosearch, the library is also usable without LeafletJS, and it does not have any dependencies whatsoever.

That last point is the interesting one, because the example the documentation leads with is not a map at all. It is an address lookup on a webshop order form, where the point is to find the closest alternative package delivery point, or to wire the field into a custom map component of your own:

```js
// import
import { OpenStreetMapProvider } from 'leaflet-geosearch';

// setup
const provider = new OpenStreetMapProvider();

// search
const results = await provider.search({ query: input.value });
```

The provider is constructed, the search takes a query object and returns a promise, and the result is an array of result objects. That is the whole library in three lines, and the Leaflet control is an optional layer on top rather than a dependency underneath.

The audience is therefore two groups: a Leaflet application that wants a search box, and any application that needs to turn a typed address into coordinates. The second group is the larger one, and it is the one the dependency free design serves.

The licence is MIT, the package is published to npm as leaflet-geosearch, the documentation and demo live at leaflet-geosearch.meijer.works, and the last push was on 2026-10-13.

## Providers are the architecture, and the result shape is the contract

The library uses so-called providers to take care of building the correct service URL and parsing the retrieved data into a uniform format, and the documentation notes that because of this architecture it is pretty easy to add your own providers, so you can use your own geocoding services.

Fourteen providers ship by default: Algolia, AMap, Bing, Esri, Geocode Earth, Google, LocationIQ, OpenCage, OpenStreetMap, PdokNl, Pelias, Mapbox, GeoApiFR and Geoapify. The OpenStreetMap one is the default and the one that needs no key, and most of the others do require API keys, with the documentation pointing at the relevant organisations for how to obtain them.

The uniform result is the contract, and its shape is worth reading closely because it is the shape your code will depend on:

```js
const result = {
  x: Number, // lon,
  y: Number, // lat,
  label: String, // formatted address
  bounds: [
    [Number, Number], // s, w - lat, lon
    [Number, Number], // n, e - lat, lon
  ],
  raw: {}, // raw provider result
};
```

So x is the longitude and y is the latitude, which is the opposite of the latitude first convention that GeoJSON uses, and the bounds are pairs of latitude and longitude for the south west and north east corners, again with latitude first. Those two details are where a provider that follows a different convention would betray you, and the normalisation is what saves you from checking each one.

The raw property is the honest part of the design. It is the unprocessed result from the third party service, it differs per provider, and the documentation says the library does not use it and includes it for developer convenience, so if you need to know what is in it you should check the third party developer documentation or look at it in your debugger. In other words, the uniform shape is the supported surface and raw is the escape hatch.

There is one special case in the provider configuration, and it is the same across all of them. Providers are unaware of any options you give them, they are simple proxies to their endpoints, and the only option that is treated specially is params, which is included in the endpoint URL. That is the mechanism for API keys, since the key has to travel in the URL, while the other attributes are for provider configuration.

## The Leaflet control and the react-leaflet wrapper

The Leaflet integration is a control that hooks the search providers into the map, and the example shows the whole of it:

```js
import L from 'leaflet';
import { GeoSearchControl } from 'leaflet-geosearch';

const searchControl = new GeoSearchControl({
  provider: provider,
});

const map = new L.Map('map');
map.addControl(searchControl);
```

The control has configurable options, such as where the search input is positioned and whether a marker should be displayed at the position of the search result.

The react-leaflet usage is similar, and the documentation names the one notable difference, the useMap hook, which is how a React component gets at the underlying map instance. The example also passes an API key through the params list, and it shows the cleanup that most examples of this kind skip:

```jsx
import { GeoSearchControl, MapBoxProvider } from 'leaflet-geosearch';
import { useMap } from 'react-leaflet';
import 'leaflet-geosearch/assets/css/leaflet.css';

const SearchField = ({ apiKey }) => {
  const provider = new MapBoxProvider({
    params: {
      access_token: apiKey,
    },
  });

  // @ts-ignore
  const searchControl = new GeoSearchControl({
    provider: provider,
  });

  const map = useMap();
  useEffect(() => {
    map.addControl(searchControl);
    return () => map.removeControl(searchControl);
  }, []);

  return null;
};
```

The effect returns a cleanup function that removes the control, and the documentation notes that the hook used in that component even allows for conditional rendering of the search field, which it then shows with a map container that includes the field only when a flag is set. That combination of a removable control and a component that can be mounted and unmounted is what makes the control usable inside a React application that changes its layout, rather than only on a static map page.

The stylesheet import is required as well, and the library ships it, so the control looks like the rest of the Leaflet interface rather than something pasted on top.

## Keys, polyfills and the browser support story

Two practical details sit between the example and a working application.

The first is keys. The repository carries an .env.sample listing the variables for the providers that need one: AMap, Bing, Geoapify, Google, HERE, LocationIQ and OpenCage. The OpenStreetMap provider is the one that works without a key, which makes it the default for a prototype and the reason the examples use it.

The second is polyfills. The documentation says the library is written with the latest technologies in mind, and that polyfills are required to support older browsers, with two recommended for IE and Safari support: a polyfill for array.includes, and unfetch for fetch requests. The unfetch one is the more consequential of the two, because fetch is the mechanism every provider uses to reach its endpoint, and a browser without a native fetch needs a polyfill rather than a different code path.

Both of these are the kind of thing that is cheap to handle once and awkward to discover later, particularly in a project that still has to support older Safari versions, which is a normal situation for a consumer web application.

There is also a hint about how the library is distributed. The manifest builds with microbundle and produces an ES module, a CommonJS bundle and a UMD bundle, with leaflet marked as an external dependency and a global mapping for the UMD build, and it publishes a compatibility bundle under a second filename. That is the shape of a library that still has to work in a plain script tag, which is consistent with the polyfill guidance and with the Leaflet plugin community it comes from.

## Building, testing and a documentation site in the repository

The build and release tooling is the part of the repository you would read to contribute rather than to use, and it is thorough.

The build script runs four jobs in parallel, a distribution build, a library build, a CSS copy and a compatibility step. The distribution build uses microbundle with leaflet external and a global mapping for the UMD output, and the library build is the TypeScript compiler, which produces the declarations. The CSS job copies the stylesheet into the distribution directory, and the compatibility job copies the UMD bundle to a second filename so that the older script tag path keeps working.

The tests run with Jest, and the lint script is a type check with the compiler in no emit mode, then ESLint over the source, the tests and the docs, then Prettier over the Markdown, MDX and JSON. Formatting and linting are therefore both enforced in one command, and the Prettier pass covers the documentation as well as the code.

The documentation site is built with docz, with dev, build and serve scripts, and a docs:update script that rebuilds it and commits the result to the gh-pages branch. The repository also carries older gatsby configuration files, which is the visible trace of the documentation having moved from one generator to another.

For an adopter, the practical consequence is that the published package is the TypeScript source compiled to several module formats with generated type declarations, and the example code in the documentation is type checked, since the react-leaflet example needs a ts-ignore comment for the control options. That last detail is a small honesty about the state of the types around the Leaflet control.

## Maintenance, versions and what the repository does not settle

The release history is regular. The tags are v4.2.1 on 2025-09-12, v4.2.2 on 2025-09-21 and v4.4.0 on 2026-03-17, and the last push was on 2026-10-13 with the repository not archived. The contributors badge counts sixty-three contributors, which is a sign of a project that has been patched by a wide group rather than maintained by one person.

There is one inconsistency worth flagging for anyone pinning a version. The package manifest in the repository records the version as 3.6.1, while the released tags are in the 4.x line and the newest is 4.4.0. So the manifest snapshot in the tree does not match the published releases, and the version you get from npm is the one to trust.

Two things the project does not settle are worth naming. It does not normalise the data quality of the providers, because it cannot: a search for an address in a country the provider does not cover will return nothing useful, and the choice of provider is a decision about your geography rather than about the library. And it does not validate the results, because the result shape is a convention rather than a contract with the providers, so the raw property is where the differences live.

For a team, the sensible way to use this is to pick the provider or providers that cover your users, treat the normalised fields as the interface your application depends on, and reach into raw only when a provider specific field is genuinely needed. The provider interface is a documented, simple proxy, so writing your own is a small job when your data needs come from a service the fourteen do not cover.

## Conclusion

leaflet-geosearch suits a Leaflet or react-leaflet application that needs an address search box, especially one that has to work across several geocoding services, because the provider adapters all return the same result shape and swapping the backend is a constructor change. It is the wrong choice if you need the search results validated against a schema, because the uniform shape is only uniform across the providers this library ships, and a poor match if you need guaranteed coverage in a specific region, since each provider has its own data quality and most of them need an API key you pay for. Before adopting, read the provider list and check which of them cover your geography, and note that the search returns raw provider data alongside the normalised fields, which is where provider specific surprises will appear.

## FAQ

### How do I use leaflet-geosearch without a map?

Construct a provider, for example the OpenStreetMapProvider, and call its search method with a query object, awaiting the promise. The library has no dependencies and works without Leaflet, so it can be used for an address lookup on a form.

### What does a geocoding result look like?

Every provider returns the same shape, with x as the longitude, y as the latitude, a label with the formatted address, bounds as the south west and north east latitude and longitude pairs, and a raw property holding the unprocessed provider result, whose contents differ per provider.

### Which geocoding providers does leaflet-geosearch support?

Fourteen ship by default: Algolia, AMap, Bing, Esri, Geocode Earth, Google, LocationIQ, OpenCage, OpenStreetMap, PdokNl, Pelias, Mapbox, GeoApiFR and Geoapify. Most need an API key, and the repository ships an .env.sample listing the variables, while the OpenStreetMap provider needs none.

### How do I use the search control with react-leaflet?

Use the useMap hook to get the map instance and add the GeoSearchControl in an effect that returns a cleanup removing the control again, and import the shipped stylesheet. The control can be mounted and unmounted conditionally, which is what lets the search field appear only in some layouts.

## Sources

- [License: MIT](https://github.com/smeijer/leaflet-geosearch/blob/main/LICENSE)
- [Project website](https://leaflet-geosearch.meijer.works)
- [README](https://github.com/smeijer/leaflet-geosearch/blob/main/README.md)
- [Releases](https://github.com/smeijer/leaflet-geosearch/releases)
- [smeijer/leaflet-geosearch on GitHub](https://github.com/smeijer/leaflet-geosearch)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/smeijer-leaflet-geosearch
