Examples

Simple input

places.js turns any HTML <input> into an autocomplete address search bar.

Selected: none

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<input type="search" id="address" class="form-control" placeholder="Where are we going?" />

<p>Selected: <strong id="address-value">none</strong></p>

<script src="https://cdn.jsdelivr.net/npm/places.js@1.4.15"></script>
<script>
(function() {
  var placesAutocomplete = places({
    container: document.querySelector('#address')
  });

  var $address = document.querySelector('#address-value')
  placesAutocomplete.on('change', function(e) {
    $address.textContent = e.suggestion.value 
  });

  placesAutocomplete.on('clear', function() {
    $address.textContent = 'none';
  });

})();
</script>

Complete form

Once the user has selected an address, you can use the change event to fill-out another form with the user address structured data.

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
<form action="/billing" class="form">
  <div class="form-group">
    <label for="form-address">Address*</label>
    <input type="search" class="form-control" id="form-address" placeholder="Where do you live?" />
  </div>
  <div class="form-group">
    <label for="form-address2">Address 2</label>
    <input type="text" class="form-control" id="form-address2" placeholder="Street number and name" />
  </div>
  <div class="form-group">
    <label for="form-city">City*</label>
    <input type="text" class="form-control" id="form-city" placeholder="City">
  </div>
  <div class="form-group">
    <label for="form-zip">ZIP code*</label>
    <input type="text" class="form-control" id="form-zip" placeholder="ZIP code">
  </div>
</form>

<script src="https://cdn.jsdelivr.net/npm/places.js@1.4.15"></script>
<script>
(function() {
  var placesAutocomplete = places({
    container: document.querySelector('#form-address'),
    type: 'address',
    templates: {
      value: function(suggestion) {
        return suggestion.name;
      }
    }
  });
  placesAutocomplete.on('change', function resultSelected(e) {
    document.querySelector('#form-address2').value = e.suggestion.administrative || '';
    document.querySelector('#form-city').value = e.suggestion.city || '';
    document.querySelector('#form-zip').value = e.suggestion.postcode || '';
  });
})();
</script>

You can build a city search input by using the type parameter.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<input type="search" id="city" class="form-control" placeholder="In which city do you live?" />

<script src="https://cdn.jsdelivr.net/npm/places.js@1.4.15"></script>
<script>
(function() {
  var placesAutocomplete = places({
    container: document.querySelector('#city'),
    type: 'city',
    aroundLatLngViaIP: false,
    templates: {
      value: function(suggestion) {
        return suggestion.name;
      }
    }
  });
})();
</script>

You can also search in countries only.

places.js turns any HTML <input> into a City as-you-type search bar. You can filter on a specific list of countries if needed:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<link rel="stylesheet" type="text/css" href="//cloud.github.com/downloads/lafeber/world-flags-sprite/flags16.css" />

<div class="f16">
  <input type="search" id="country" class="form-control" placeholder="What's your favorite country?" />
</div>

<script src="https://cdn.jsdelivr.net/npm/places.js@1.4.15"></script>
<script>
(function() {
  var placesAutocomplete = places({
    container: document.querySelector('#country'),
    type: 'country',
    templates: {
      suggestion: function(suggestion) {
        return '<i class="flag ' + suggestion.countryCode + '"></i> ' +
          suggestion.highlight.name;
      }
    }
  });
})();
</script>

We will use the Leaflet JavaScript library as an example to display the places.js results on the map and update them when needed.

To try this example, you need to add leaflet in your code:

1
2
<link rel="stylesheet" href="https://cdn.jsdelivr.net/leaflet/1/leaflet.css" />
<script src="https://cdn.jsdelivr.net/leaflet/1/leaflet.js"></script>

Then use this:

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
92
93
94
95
96
97
98
<div id="map-example-container"></div>
<input type="search" id="input-map" class="form-control" placeholder="Where are we going?" />

<style>
  #map-example-container {height: 300px};
</style>

<script src="https://cdn.jsdelivr.net/npm/places.js@1.4.15"></script>
<script>
(function() {
  var placesAutocomplete = places({
    container: document.querySelector('#input-map')
  });

  var map = L.map('map-example-container', {
    scrollWheelZoom: false,
    zoomControl: false
  });

  var osmLayer = new L.TileLayer(
    'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
      minZoom: 1,
      maxZoom: 13,
      attribution: 'Map data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors'
    }
  );

  var markers = [];

  map.setView(new L.LatLng(0, 0), 1);
  map.addLayer(osmLayer);

  placesAutocomplete.on('suggestions', handleOnSuggestions);
  placesAutocomplete.on('cursorchanged', handleOnCursorchanged);
  placesAutocomplete.on('change', handleOnChange);
  placesAutocomplete.on('clear', handleOnClear);

  function handleOnSuggestions(e) {
    markers.forEach(removeMarker);
    markers = [];

    if (e.suggestions.length === 0) {
      map.setView(new L.LatLng(0, 0), 1);
      return;
    }

    e.suggestions.forEach(addMarker);
    findBestZoom();
  }

  function handleOnChange(e) {
    markers
      .forEach(function(marker, markerIndex) {
        if (markerIndex === e.suggestionIndex) {
          markers = [marker];
          marker.setOpacity(1);
          findBestZoom();
        } else {
          removeMarker(marker);
        }
      });
  }
  
  function handleOnClear() {
    map.setView(new L.LatLng(0, 0), 1);
    markers.forEach(removeMarker);
  }

  function handleOnCursorchanged(e) {
    markers
      .forEach(function(marker, markerIndex) {
        if (markerIndex === e.suggestionIndex) {
          marker.setOpacity(1);
          marker.setZIndexOffset(1000);
        } else {
          marker.setZIndexOffset(0);
          marker.setOpacity(0.5);
        }
      });
  }

  function addMarker(suggestion) {
    var marker = L.marker(suggestion.latlng, {opacity: .4});
    marker.addTo(map);
    markers.push(marker);
  }

  function removeMarker(marker) {
    map.removeLayer(marker);
  }

  function findBestZoom() {
    var featureGroup = L.featureGroup(markers);
    map.fitBounds(featureGroup.getBounds().pad(0.5), {animate: false});
  }
})();
</script>

Force lat, lng, radius

Reusing the map example, we want to only search around Paris, France.

This is useful when you really want to display results around a specific area.

To try this example, you need to add leaflet in your code:

1
2
<link rel="stylesheet" href="https://cdn.jsdelivr.net/leaflet/1/leaflet.css" />
<script src="https://cdn.jsdelivr.net/leaflet/1/leaflet.js"></script>

Then use this:

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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
<div id="map-example-container-paris"></div>
<input type="search" id="input-map-paris" class="form-control" placeholder="Find a street in Paris, France. Try &quot;Rivoli&quot;" />

<style>
  #map-example-container-paris {
    height: 300px
  }
</style>

<script src="https://cdn.jsdelivr.net/npm/places.js@1.4.15"></script>
<script>
(function() {
  var latlng = {
    lat: 48.8566,
    lng: 2.34287
  };

  var placesAutocomplete = places({
    container: document.querySelector('#input-map-paris'),
    aroundLatLng: latlng.lat + ',' + latlng.lng, // Paris latitude longitude
    aroundRadius: 10 * 1000, // 10km radius
    type: 'address'
  });

  var map = L.map('map-example-container-paris', {
    scrollWheelZoom: false,
    zoomControl: false
  });

  var osmLayer = new L.TileLayer(
    'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
      minZoom: 12,
      maxZoom: 18,
      attribution: 'Map data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors'
    }
  );

  var markers = [];

  map.setView(new L.LatLng(latlng.lat, latlng.lng), 12);
  map.addLayer(osmLayer);

  placesAutocomplete.on('suggestions', handleOnSuggestions);
  placesAutocomplete.on('cursorchanged', handleOnCursorchanged);
  placesAutocomplete.on('change', handleOnChange);

  function handleOnSuggestions(e) {
    markers.forEach(removeMarker);
    markers = [];

    if (e.suggestions.length === 0) {
      map.setView(new L.LatLng(latlng.lat, latlng.lng), 12);
      return;
    }

    e.suggestions.forEach(addMarker);
    findBestZoom();
  }

  function handleOnChange(e) {
    markers
      .forEach(function(marker, markerIndex) {
        if (markerIndex === e.suggestionIndex) {
          markers = [marker];
          marker.setOpacity(1);
          findBestZoom();
        } else {
          removeMarker(marker);
        }
      });
  }

  function handleOnClear() {
    map.setView(new L.LatLng(latlng.lat, latlng.lng), 12);
  }

  function handleOnCursorchanged(e) {
    markers
      .forEach(function(marker, markerIndex) {
        if (markerIndex === e.suggestionIndex) {
          marker.setOpacity(1);
          marker.setZIndexOffset(1000);
        } else {
          marker.setZIndexOffset(0);
          marker.setOpacity(0.5);
        }
      });
  }

  function addMarker(suggestion) {
    var marker = L.marker(suggestion.latlng, {opacity: .4});
    marker.addTo(map);
    markers.push(marker);
  }

  function removeMarker(marker) {
    map.removeLayer(marker);
  }

  function findBestZoom() {
    var featureGroup = L.featureGroup(markers);
    map.fitBounds(featureGroup.getBounds().pad(0.5), {animate: false});
  }
})();
</script>

Advanced

Templates

You can customize both the input value and dropdown suggestion templates.

Templates are functions called with a suggestion object.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<input type="search" id="address-templates" class="form-control" placeholder="Where are we going?" />

<script src="https://cdn.jsdelivr.net/npm/places.js@1.4.15"></script>
<script>
(function() {
  var placesAutocomplete = places({
    container: document.querySelector('#address-templates'),
    templates: {
      value: function(suggestion) {
        return 'Maybe ' + suggestion.name + ' in ' + suggestion.country + '?';
      },
      suggestion: function(suggestion) {
        return '<u>Click here to select ' + suggestion.name + ' from ' + suggestion.country + '</u>';
      }
    }
  });
})();
</script>

Disable styling

The default Algolia Places styling can be enhanced by overriding the default rules.

If you need full control and want to disable all default styling, you can also do it.

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
<div id="input-styling-address">
  <input type="search" placeholder="Where are we going?" />
</div>

<script src="https://cdn.jsdelivr.net/npm/places.js@1.4.15"></script>
<style>
#input-styling-address input {
  display: inline-block;
  border: 1px solid #d9d9d9;
  border-radius: 12px;
  background: #ffffff;
  padding: 1em 0 1em 45px;
  width: 100%;
}

#input-styling-address input:focus, #input-styling-address input:active {
  outline: 0;
  border-color: #aaaaaa;
  background: #ffffff;
}

#input-styling-address .ap-nostyle-dropdown-menu {
  box-shadow: none;
  border: 1px solid #dadada;
  border-radius: 0;
  background: #fff;
  width: 100%;
}

#input-styling-address .ap-nostyle-input-icon {
  display: block;
  position: absolute;
  background: none;
  border: none;
}

#input-styling-address .algolia-places-nostyle { width: 50%; }
#input-styling-address .ap-nostyle-icon-pin { left: 5px;top: 10px; }
#input-styling-address .ap-nostyle-icon-clear { right: 5px;top: 15px }
#input-styling-address input:hover { border-color: silver; }
#input-styling-address input::placeholder { color: #aaaaaa; }
#input-styling-address .ap-nostyle-suggestion { border-bottom: 1px solid #efefef; }
</style>

<script>
(function() {
  var placesAutocomplete = places({
    container: document.querySelector('#input-styling-address input'),
    style: false,
    debug: true
  });
})();
</script>

See our documentation about styling for more details.

autocomplete.js

Using Algolia's autocomplete.js library, you can search in your own data along with showing Algolia Places results.

You need an Algolia account to do so and the Algolia Places dataset:

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
92
93
94
95
96
97
98
99
100
<input type="search" id="autocomplete-dataset" class="form-control" placeholder="Search for vacation rentals or cities" />

<script src="https://cdn.jsdelivr.net/algoliasearch/3/algoliasearchLite.min.js"></script>
<script src="https://cdn.jsdelivr.net/autocomplete.js/0/autocomplete.js"></script>
<style>
.algolia-autocomplete {
  width: 100%;
}
.ad-example-dropdown-menu {
  width: 100%;
  color: black;
  background-color: #fff;
  border: 1px solid #ccc;
  border-top: none;
  border-radius: 5px;
  padding: .5em;
  box-shadow: 1px 1px 32px -10px rgba(0,0,0,0.62);
}
.ad-example-dropdown-menu .ad-example-suggestion {
  cursor: pointer;
  padding: 5px 4px;
}
.ad-example-dropdown-menu .ad-example-suggestion img {
  height: 2em;
  margin-top: .5em;
  margin-right: 10px;
  float: left;
}
.ad-example-dropdown-menu .ad-example-suggestion small {
  font-size: .8em;
  color: #bbb;
}
.ad-example-dropdown-menu .ad-example-suggestion.ad-example-cursor {
  background-color: #B2D7FF;
}
.ad-example-dropdown-menu .ad-example-suggestion em {
  font-weight: bold;
  font-style: normal;
}
.ad-example-header {
  font-weight: bold;
  padding: .5em 0;
  margin-bottom: 1em;
  border-bottom: 1px solid #ccc;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/places.js@1.4.15/dist/cdn/placesAutocompleteDataset.min.js"></script>
<script>
(function() {
  var client = algoliasearch('latency', '6be0576ff61c053d5f9a3225e2a90f76');
  var index = client.initIndex('airbnb');

  // create the first autocomplete.js dataset: vacation rentals
  var rentalsDataset = {
    source: autocomplete.sources.hits(index, {hitsPerPage: 2}),
    displayKey: 'name',
    name: 'rentals',
    templates: {
      header: '<div class="ad-example-header">Vacation rentals</div>',
      suggestion: function(suggestion) {
        return '<img src="' + suggestion.thumbnail_url + '" />' +
          '<div>' +
            suggestion._highlightResult.name.value + '<br />' +
            '<small>' + suggestion._highlightResult.city.value + '</small>' +
         '</div>';
      }
    }
  };

  // create the second dataset: places
  // we automatically inject the default CSS
  // all the places.js options are available
  var placesDataset = placesAutocompleteDataset({
    algoliasearch: algoliasearch,
    templates: {
      header: '<div class="ad-example-header">Cities</div>'
    },
    hitsPerPage: 3
  });

  // init
  var autocompleteInstance = autocomplete(document.querySelector('#autocomplete-dataset'), {
    hint: false,
    debug: true,
    cssClasses: {prefix: 'ad-example'}
  }, [
    rentalsDataset,
    placesDataset
  ]);

  var autocompleteChangeEvents = ['selected', 'autocompleted'];

  autocompleteChangeEvents.forEach(function(eventName) {
    autocompleteInstance.on('autocomplete:'+ eventName, function(event, suggestion, datasetName) {
      console.log(datasetName, suggestion);
    });
  });
})();
</script>

See the documentation about the placesAutocompleteDataset function.

instantsearch.js

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
<input type="search" id="input-map-instantsearch" class="form-control" placeholder="Where are you looking for a coffee?" />
<div id="map-instantsearch-container"></div>

<style>
  #map-instantsearch-container {height: 300px};
</style>

<script src="https://cdn.jsdelivr.net/instantsearch.js/1.6.0/instantsearch.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/places.js@1.4.15/dist/cdn/placesInstantsearchWidget.min.js"></script>
<script src="https://cdn.jsdelivr.net/instantsearch-googlemaps/1.2.4/instantsearch-googlemaps.min.js"></script>
<script src="https://maps.googleapis.com/maps/api/js"></script>
<script>
(function() {
var search = instantsearch({
  appId: 'latency',
  apiKey: 'ffc36feb6e9df06e1c3c4549b5af2b31',
  indexName: 'starbucks',
  searchParameters: {
    hitsPerPage: 50
  }
});

var searchBox = placesInstantsearchWidget({
  container: document.querySelector('#input-map-instantsearch')
});

var map = instantsearchGoogleMaps({
  container: document.querySelector('#map-instantsearch-container'),
  prepareMarkerData: ({Brand, Name, 'Street Combined': street}) => {
    return {
      title: `${Brand} - ${Name}`
    };
  },
  refineOnMapInteraction: true
});

search.addWidget(searchBox);
search.addWidget(map);
search.start();
})();
</script>