geoSearch
geoSearch({
  container: string|HTMLElement,
  googleReference: object,
  // Optional parameters
  initialZoom: number,
  initialPosition: object,
  mapOptions: object,
  builtInMarker: object,
  customHTMLMarker: object,
  enableRefine: boolean,
  enableClearMapRefinement: boolean,
  enableRefineControl: boolean,
  enableRefineOnMapMove: boolean,
  templates: object,
  cssClasses: object,
});
        1
import { geoSearch } from 'instantsearch.js/es/widgets';
About this widget
The geoSearch widget displays search results on a Google Map. 
It lets you search for results based on their position and provides some common usage patterns such as “search on map interactions”.
Requirements
The widget uses the geo search capabilities of Algolia. 
Your hits must have a _geoloc attribute so they can be displayed on the map.
The feature is currently incompatible with several values in the _geoloc attribute.
You are responsible for loading the Google Maps library, it doesn’t come with InstantSearch.js. You need to load the library and pass a reference to the widget. You can find more information about how to install the library in the Google Maps documentation.
Make sure that you explicitly set the height of the map container (see below), otherwise it won’t show.
1
2
3
.ais-GeoSearch-map {
  height: 500px; /* You can change this height */
}
Examples
1
2
3
4
geoSearch({
  container: '#geo-search',
  googleReference: window.google,
});
Options
| Parameter | Description | ||
|---|---|---|---|
          
            container
          
         | 
        
           
                
                type: string|HTMLElement
                
               
              
                
                        Required
                
               
          The CSS Selector or   | 
      ||
| 
           
Copy
 
       | 
      |||
          
            googleReference
          
         | 
        
           
                
                type: object
                
               
              
                
                        Required
                
               
          The reference to the global  See the Google Maps documentation for more information.  | 
      ||
| 
           
Copy
 
       | 
      |||
          
            initialZoom
          
         | 
        
           
                
                type: number
                
               
              
                
                  default: 1
                
               
              
                
                    Optional
                
               
          By default, the map sets the zoom based on to the markers that are displayed on it. Yet when InstantSearch.js refines the results, they may be empty. When it happens, it needs a zoom level to render the map.  | 
      ||
| 
           
Copy
 
       | 
      |||
          
            initialPosition
          
         | 
        
           
                
                type: object
                
               
              
                
                  default: { lat: 0, lng: 0 }
                
               
              
                
                    Optional
                
               
          By default, the map sets the position based on to the markers that are displayed on it. Yet when InstantSearch.js refines the results, they may be empty. When it happens, it needs a position to render the map.  | 
      ||
| 
           
Copy
 
       | 
      |||
          
            mapOptions
          
         | 
        
           
                
                type: object
                
               
              
                
                    Optional
                
               
          The options forwarded to the Google Maps constructor. See the Google Maps documentation for more information.  | 
      ||
| 
           
Copy
 
       | 
      |||
          
            builtInMarker
          
         | 
        
           
                
                type: object
                
               
              
                
                    Optional
                
               
          The options for customizing the built-in Google Maps markers. This is ignored when the  
  | 
      ||
| 
           
Copy
 
       | 
      |||
          
            customHTMLMarker
          
         | 
        
           
                
                type: object
                
               
              
                
                    Optional
                
               
          The options for customizing the HTML marker. InstantSearch.js provides an alternative to the built-in Google Maps markers to give a full control over the marker rendering. You can use plain HTML to build your marker (see  
  | 
      ||
| 
           
Copy
 
       | 
      |||
          
            enableRefine
          
         | 
        
           
                
                type: boolean
                
               
              
                
                  default: true
                
               
              
                
                    Optional
                
               
          If   | 
      ||
| 
           
Copy
 
       | 
      |||
          
            enableClearMapRefinement
          
         | 
        
           
                
                type: boolean
                
               
              
                
                  default: true
                
               
              
                
                    Optional
                
               
          If   | 
      ||
| 
           
Copy
 
       | 
      |||
          
            enableRefineControl
          
         | 
        
           
                
                type: boolean
                
               
              
                
                  default: true
                
               
              
                
                    Optional
                
               
          If   | 
      ||
| 
           
Copy
 
       | 
      |||
          
            enableRefineOnMapMove
          
         | 
        
           
                
                type: boolean
                
               
              
                
                  default: true
                
               
              
                
                    Optional
                
               
          If   | 
      ||
| 
           
Copy
 
       | 
      |||
          
            templates
          
         | 
        
           
                
                type: object
                
               
              
                
                    Optional
                
               
          The templates to use for the widget.  | 
      ||
| 
           
Copy
 
       | 
      |||
          
            cssClasses
          
         | 
        
           
                
                type: object
                
               
              
                
                  default: {}
                
               
              
                
                    Optional
                
               
          The CSS classes you can override: 
  | 
      ||
| 
           
Copy
 
       | 
      |||
Templates
You can customize parts of the widget’s UI using the Templates API.
Every template provides an html function you can use as a tagged template. Using html lets you safely provide templates as an HTML string. It works directly in the browser without a build step. See Templating your UI for more information.
The html function is available starting from v4.46.0.
| Parameter | Description | ||
|---|---|---|---|
          
            HTMLMarker
          
         | 
        
           
                
                type: string|function
                
               
              
                
                    Optional
                
               
          The template to use for the marker.  | 
      ||
| 
           
Copy
 
       | 
      |||
          
            reset
          
         | 
        
           
                
                type: string|function
                
               
              
                
                    Optional
                
               
          The template for the reset button.  | 
      ||
| 
           
Copy
 
       | 
      |||
          
            toggle
          
         | 
        
           
                
                type: string|function
                
               
              
                
                    Optional
                
               
          The template for the toggle label.  | 
      ||
| 
           
Copy
 
       | 
      |||
          
            redo
          
         | 
        
           
                
                type: string|function
                
               
              
                
                    Optional
                
               
          The template for the redo label.  | 
      ||
| 
           
Copy
 
       | 
      |||
HTML output
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div class="ais-GeoSearch">
  <div class="ais-GeoSearch-map">
    <!-- Map element here -->
  </div>
  <div class="ais-GeoSearch-control">
    <label class="ais-GeoSearch-label">
      <input class="ais-GeoSearch-input" type="checkbox">
      Search as I move the map
    </label>
  </div>
  <button class="ais-GeoSearch-reset">
    Clear the map refinement
  </button>
</div>
Customize the UI with connectGeoSearch
If you want to create your own UI of the geoSearch widget, you can use connectors.
To use connectGeoSearch, you can import it with the declaration relevant to how you installed InstantSearch.js.
1
import { connectGeoSearch } from 'instantsearch.js/es/connectors';
Then it’s a 3-step process:
// 1. Create a render function
const renderGeoSearch = (renderOptions, isFirstRender) => {
  // Rendering logic
};
// 2. Create the custom widget
const customGeoSearch = connectGeoSearch(
  renderGeoSearch
);
// 3. Instantiate
search.addWidgets([
  customGeoSearch({
    // instance params
  })
]);
  Create a render function
This rendering function is called before the first search (init lifecycle step)
and each time results come back from Algolia (render lifecycle step).
const renderGeoSearch = (renderOptions, isFirstRender) => {
  const {
    object[] items,
    object position,
    object currentRefinement,
    function refine,
    function sendEvent,
    function clearMapRefinement,
    function isRefinedWithMap,
    function toggleRefineOnMapMove,
    function isRefineOnMapMove,
    function setMapMoveSinceLastRefine,
    function hasMapMoveSinceLastRefine,
    object widgetParams,
  } = renderOptions;
  if (isFirstRender) {
    // Do some initial rendering and bind events
  }
  // Render the widget
}
  Rendering options
The examples built with the connector use Leaflet to render the map. Make sure to have the library correctly setup before trying the demo. You can find more details in the Leaflet documentation. We picked Leaflet but you can use any library you prefer (e.g., Google Maps, Mapbox, etc.)
| Parameter | Description | ||
|---|---|---|---|
          
            items
          
         | 
        
           
                
                type: object[]
                
               
          The hits that matched the search request.  | 
      ||
| 
           
Copy
 
OpenStreetMap contributors',
    }).addTo(map);
  }
  markers.forEach(marker => marker.remove());
  markers = items.map(({ _geoloc }) =>
    L.marker([_geoloc.lat, _geoloc.lng]).addTo(map)
  );
  if (markers.length) {
    map.fitBounds(L.featureGroup(markers).getBounds());
  }
};
" class="snippet-body ">
 
 | 
      |||
          
            position
          
         | 
        
           
                
                type: object
                
               
          The current position of the search, when applicable.  | 
      ||
| 
           
Copy
 
OpenStreetMap contributors',
    }).addTo(map);
  }
  markers.forEach(marker => marker.remove());
  markers = items.map(({ _geoloc }) =>
    L.marker([_geoloc.lat, _geoloc.lng]).addTo(map)
  );
  if (markers.length) {
    map.fitBounds(L.featureGroup(markers).getBounds());
  } else {
    map.setView(
      position || {
        lat: 48.864716,
        lng: 2.349014,
      },
      12
    );
  }
};
" class="snippet-body ">
 
 | 
      |||
          
            currentRefinement
          
         | 
        
           
                
                type: object
                
               
          The current bounding box of the search, with: 
  | 
      ||
          
            refine
          
         | 
        
           
                
                type: function
                
               
          Sets a bounding box to filter the results from the given map bounds. The function accepts an object with: 
  | 
      ||
          
            sendEvent
          
         | 
        
           
                
                type: (eventType, hit, eventName) => void
                
               
          The function to send  
  | 
      ||
| 
           
Copy
 
 | 
      |||
          
            clearMapRefinement
          
         | 
        
           
                
                type: function
                
               
          Resets the current bounding box refinement.  | 
      ||
          
            isRefinedWithMap
          
         | 
        
           
                
                type: function
                
               
          Returns   | 
      ||
          
            toggleRefineOnMapMove
          
         | 
        
           
                
                type: function
                
               
          Toggles whether the user is able to refine on map move.  | 
      ||
          
            isRefineOnMapMove
          
         | 
        
           
                
                type: function
                
               
          Returns   | 
      ||
          
            setMapMoveSinceLastRefine
          
         | 
        
           
                
                type: function
                
               
          Sets whether the map has moved since the last refinement. This should be call on each map move. The call to the function triggers a new render only when the value changes.  | 
      ||
          
            hasMapMoveSinceLastRefine
          
         | 
        
           
                
                type: function
                
               
          Returns   | 
      ||
          
            widgetParams
          
         | 
        
           
                
                type: object
                
               
          All original widget options forwarded to the render function.  | 
      ||
Create and instantiate the custom widget
We first create custom widgets from our rendering function, then we instantiate them. When doing that, there are two types of parameters you can give:
- Instance parameters: they are predefined parameters that you can use to configure the behavior of Algolia.
 - Your own parameters: to make the custom widget generic.
 
Both instance and custom parameters are available in connector.widgetParams, inside the renderFunction.
const customGeoSearch = connectGeoSearch(
  renderGeoSearch
);
search.addWidgets([
  customGeoSearch({
    // Optional parameters
    enableRefineOnMapMove: boolean,
    transformItems: function,
  })
]);
  Instance options
| Parameter | Description | ||
|---|---|---|---|
          
            enableRefineOnMapMove
          
         | 
        
           
                
                type: boolean
                
               
              
                
                  default: true
                
               
              
                
                    Optional
                
               
          If   | 
      ||
| 
           
Copy
 
       | 
      |||
          
            transformItems
          
         | 
        
           
                
                type: function
                
               
              
                
                  default: items => items
                
               
              
                
                    Optional
                
               
          Receives the items and is called before displaying them. Should return a new array with the same shape as the original array. Useful for transforming, removing, or reordering items. In addition, the full   | 
      ||
| 
           
Copy
 
       | 
      |||
Full example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
// Create the render function
let map = null;
let markers = [];
let isUserInteraction = true;
const renderGeoSearch = (renderOptions, isFirstRendering) => {
  const {
    items,
    currentRefinement,
    refine,
    clearMapRefinement,
    widgetParams,
  } = renderOptions;
  const {
    initialZoom,
    initialPosition,
    container,
  } = widgetParams;
  if (isFirstRendering) {
    const element = document.createElement('div');
    element.style.height = '100%';
    const button = document.createElement('button');
    button.textContent = 'Clear the map refinement';
    map = L.map(element);
    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
      attribution:
        '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors',
    }).addTo(map);
    map.on('moveend', () => {
      if (isUserInteraction) {
        const ne = map.getBounds().getNorthEast();
        const sw = map.getBounds().getSouthWest();
        refine({
          northEast: { lat: ne.lat, lng: ne.lng },
          southWest: { lat: sw.lat, lng: sw.lng },
        });
      }
    });
    button.addEventListener('click', () => {
      clearMapRefinement();
    });
    container.appendChild(element);
    container.appendChild(button);
  }
  container.querySelector('button').hidden = !currentRefinement;
  markers.forEach(marker => marker.remove());
  markers = items.map(({ _geoloc }) =>
    L.marker([_geoloc.lat, _geoloc.lng]).addTo(map)
  );
  isUserInteraction = false;
  if (!currentRefinement && markers.length) {
    map.fitBounds(L.featureGroup(markers).getBounds(), {
      animate: false,
    });
  } else if (!currentRefinement) {
    map.setView(initialPosition, initialZoom, {
      animate: false,
    });
  }
  isUserInteraction = true;
};
// Create the custom widget
const customGeoSearch = connectGeoSearch(
  renderGeoSearch
);
// Instantiate the custom widget
search.addWidgets([
  customGeoSearch({
    container: document.querySelector('#geo-search'),
    initialZoom: 12,
    initialPosition: {
      lat: 48.864716,
      lng: 2.349014,
    },
  })
]);