Skip to main content
The useGeoSearch hook provides the logic to build a custom geographic search component with map integration.

Import

import { useGeoSearch } from 'react-instantsearch';

Parameters

initialZoom
number
default:"1"
Initial zoom level for the map.
const hook = useGeoSearch({ initialZoom: 12 });
initialPosition
{ lat: number, lng: number }
Initial center position for the map.
const hook = useGeoSearch({
  initialPosition: { lat: 40.7128, lng: -74.0060 },
});
enableRefineOnMapMove
boolean
default:"true"
Whether to refine search when the map is moved.
transformItems
(items: Hit[]) => Hit[]
Function to transform the items with geo coordinates.

Returns

items
Hit[]
The list of items with geographic coordinates.
const { items } = useGeoSearch();
position
{ lat: number, lng: number }
The current map center position.
currentRefinement
BoundingBox
The current geographic bounding box refinement.
refine
(bounds: BoundingBox) => void
Function to refine the search by geographic bounds.
clearMapRefinement
() => void
Function to clear the geographic refinement.
isRefineOnMapMove
boolean
Whether refinement on map move is enabled.
toggleRefineOnMapMove
() => void
Function to toggle refinement on map move.
setMapMoveSinceLastRefine
() => void
Function to indicate the map has moved.
hasMapMoveSinceLastRefine
boolean
Whether the map has moved since the last refinement.

Examples

Basic Google Maps Integration

import { useGeoSearch } from 'react-instantsearch';
import { GoogleMap, Marker } from '@react-google-maps/api';

function GeoSearchMap() {
  const {
    items,
    refine,
    clearMapRefinement,
  } = useGeoSearch({
    initialZoom: 12,
    initialPosition: { lat: 40.7128, lng: -74.0060 },
  });

  const handleBoundsChanged = (map) => {
    const bounds = map.getBounds();
    if (bounds) {
      refine({
        northEast: {
          lat: bounds.getNorthEast().lat(),
          lng: bounds.getNorthEast().lng(),
        },
        southWest: {
          lat: bounds.getSouthWest().lat(),
          lng: bounds.getSouthWest().lng(),
        },
      });
    }
  };

  return (
    <div>
      <GoogleMap
        onBoundsChanged={handleBoundsChanged}
        zoom={12}
        center={{ lat: 40.7128, lng: -74.0060 }}
      >
        {items.map((item) => (
          <Marker
            key={item.objectID}
            position={{
              lat: item._geoloc.lat,
              lng: item._geoloc.lng,
            }}
            title={item.name}
          />
        ))}
      </GoogleMap>
      <button onClick={clearMapRefinement}>Clear Map Filter</button>
    </div>
  );
}

Store Locator

import { useGeoSearch } from 'react-instantsearch';
import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet';

function StoreLocator() {
  const { items, position, refine } = useGeoSearch({
    initialZoom: 10,
    initialPosition: { lat: 40.7128, lng: -74.0060 },
  });

  return (
    <div className="store-locator">
      <div className="map-container">
        <MapContainer
          center={[position.lat, position.lng]}
          zoom={10}
        >
          <TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
          {items.map((store) => (
            <Marker
              key={store.objectID}
              position={[store._geoloc.lat, store._geoloc.lng]}
            >
              <Popup>
                <h3>{store.name}</h3>
                <p>{store.address}</p>
                <p>{store.phone}</p>
              </Popup>
            </Marker>
          ))}
        </MapContainer>
      </div>
      <div className="store-list">
        <h3>Nearby Stores</h3>
        {items.map((store) => (
          <div key={store.objectID} className="store-card">
            <h4>{store.name}</h4>
            <p>{store.address}</p>
            <p>{store.phone}</p>
          </div>
        ))}
      </div>
    </div>
  );
}

Search Within Radius

import { useGeoSearch } from 'react-instantsearch';
import { useState } from 'react';

function RadiusSearch() {
  const { items, refine } = useGeoSearch();
  const [center, setCenter] = useState({ lat: 40.7128, lng: -74.0060 });
  const [radius, setRadius] = useState(5000); // 5km

  const handleSearch = () => {
    refine({
      aroundLatLng: `${center.lat}, ${center.lng}`,
      aroundRadius: radius,
    });
  };

  return (
    <div>
      <div className="search-controls">
        <input
          type="number"
          value={center.lat}
          onChange={(e) => setCenter({ ...center, lat: Number(e.target.value) })}
          placeholder="Latitude"
        />
        <input
          type="number"
          value={center.lng}
          onChange={(e) => setCenter({ ...center, lng: Number(e.target.value) })}
          placeholder="Longitude"
        />
        <select value={radius} onChange={(e) => setRadius(Number(e.target.value))}>
          <option value="1000">1 km</option>
          <option value="5000">5 km</option>
          <option value="10000">10 km</option>
          <option value="50000">50 km</option>
        </select>
        <button onClick={handleSearch}>Search</button>
      </div>
      <div className="results">
        <p>Found {items.length} results within {radius / 1000}km</p>
      </div>
    </div>
  );
}

TypeScript

import { useGeoSearch } from 'react-instantsearch';
import type { UseGeoSearchProps } from 'react-instantsearch';

interface Location {
  objectID: string;
  name: string;
  _geoloc: {
    lat: number;
    lng: number;
  };
}

function GeoSearchMap(props?: UseGeoSearchProps) {
  const { items } = useGeoSearch<Location>(props);

  return (
    <div>
      {items.map((item) => (
        <div key={item.objectID}>
          {item.name} - {item._geoloc.lat}, {item._geoloc.lng}
        </div>
      ))}
    </div>
  );
}

Build docs developers (and LLMs) love