This is a React hook for Google Maps Places Autocomplete, which helps you build a UI component with the feature of place autocomplete easily! By leveraging the power of Google Maps Places API, you can provide a great UX (user experience) for user interacts with your search bar or form, etc. Hope you guys ππ» it.
β€οΈ it? βοΈ it on GitHub or Tweet about it.
β‘οΈ Try yourself: https://use-places-autocomplete.netlify.app
- π§ Provides intelligent places suggestions powered by Google Maps Places API.
- π£ Builds your own customized autocomplete UI by React hook.
- π§ Utility functions to do geocoding and get geographic coordinates using Google Maps Geocoding API.
- π€ Built-in cache mechanism for you to save the cost of Google APIs.
- π° Built-in debounce mechanism for you to lower the cost of Google APIs.
- π Supports asynchronous Google script loading.
- π Supports TypeScript type definition.
- β¨οΈ Builds a UX-rich component (e.g. WAI-ARIA compliant and keyword support) via comprehensive demo code.
- π¦ Tiny size
(~ 1.7KB gzipped).
No external dependencies, aside from the
react.
To use use-places-autocomplete, you must use react@16.8.0 or greater which
includes hooks.
This package is distributed via npm.
$ yarn add use-places-autocomplete
# or
$ npm install --save use-places-autocompleteWhen working with TypeScript you need to install the
@types/google.maps as a
devDependencies.
$ yarn add --dev @types/google.maps
# or
$ npm install --save-dev @types/google.mapsusePlacesAutocomplete is based on the
Places Autocomplete
(or more specific
docs)
of
Google Maps Place API.
If you are unfamiliar with these APIs, we recommend you review them before we
start.
To use this hook, there're two things we need to do:
In the new version of use-places-autocomplete, this package is using the
@googlemaps/js-api-loader
package to internally load the places API, without needing a script tag. Now
all you need to do is pass in the apiKey field into the hook's config:
import { usePlacesAutocomplete } from 'use-places-autocomplete';
const placesAutocomplete = usePlacesAutocomplete({
apiKey: 'MY_API_KEY',
});Now we can start to build our component. Check the API out to learn more.
import type { ChangeEvent } from "react";
import {
usePlacesAutocomplete,
type Suggestion,
} from "use-places-autocomplete";
const PlacesAutocomplete = () => {
const {
ready,
value,
suggestions: { status, data },
setValue,
completeSession,
} = usePlacesAutocomplete({
apiKey: "YOUR_API_KEY",
requestOptions: {
/* Define search scope here */
},
debounce: 300,
});
const handleInput = (e: ChangeEvent<HTMLInputElement>) => {
// Update the keyword of the input element
setValue(e.target.value);
};
const handleSelect =
(suggestion: Suggestion) =>
async () => {
setValue(suggestion.text.text, false);
// First, convert the suggestion to a place
const place = suggestion.toPlace();
// Then, fetch the fields you want from the place
await place.fetchFields({ fields: ["addressComponents", "formattedAddress"] });
// Complete the autocomplete session so a new one can be started
// https://developers.google.com/maps/documentation/places/web-service/place-session-tokens
completeSession();
// Finally, use those fields how you want
console.log(place.formattedAddress);
console.log(place.addressComponents);
};
const renderSuggestions = () =>
data.map((suggestion) => {
const {
placeId,
mainText,
secondaryText,
} = suggestion;
return (
<li key={placeId} onClick={handleSelect(suggestion)}>
<strong>{mainText?.text}</strong> <small>{secondaryText?.text}</small>
</li>
);
});
return (
<div>
<input
value={value}
onChange={handleInput}
disabled={!ready}
placeholder="Where are you going?"
/>
{/* We can use the "status" to decide whether we should display the dropdown or not */}
{status === "OK" && <ul>{renderSuggestions()}</ul>}
</div>
);
};Easy right? This is the magic of usePlacesAutocomplete β¨. I just showed you
how it works via a minimal example. However, you can build a UX rich
autocomplete component, like
WAI-ARIA compliant
and keyword interaction like my demo, by checking the
code or integrating this hook with the
combobox of
Reach UI to achieve that.
import {
Combobox,
ComboboxInput,
ComboboxList,
ComboboxOption,
ComboboxPopover,
} from '@reach/combobox';
import '@reach/combobox/styles.css';
import usePlacesAutocomplete from 'use-places-autocomplete';
const PlacesAutocomplete = () => {
const {
ready,
value,
suggestions: { status, data },
setValue,
} = usePlacesAutocomplete({ apiKey: 'YOUR_API_KEY' });
const handleInput = (e) => {
setValue(e.target.value);
};
const handleSelect = (val) => {
setValue(val, false);
};
return (
<Combobox onSelect={handleSelect} aria-labelledby="demo">
<ComboboxInput value={value} onChange={handleInput} disabled={!ready} />
<ComboboxPopover>
<ComboboxList>
{status === 'OK' &&
data.map(({ placeId, text }) => (
<ComboboxOption key={placeId} value={test.text} />
))}
</ComboboxList>
</ComboboxPopover>
</Combobox>
);
};By default, this library caches the response data to help you save the cost of Google Maps Places API and optimize search performance.
const methods = usePlacesAutocomplete({
// Provide the cache time in seconds, the default is 24 hours
cache: 24 * 60 * 60,
});const returnObj = usePlacesAutocomplete(parameterObj);When using usePlacesAutocomplete, you can configure the following options via
the parameter.
| Key | Type | Default | Description |
|---|---|---|---|
apiKey |
string | The API key from GCP that is set up to work with the Places API (New). This is a required argument to use the hook. | |
requestOptions |
object | The request options of Google Maps Places API except for input (e.g. bounds, radius, etc.). |
|
debounce |
number | 200 |
Number of milliseconds to delay before making a request to Google Maps Places API. |
cache |
number | false | 86400 (24 hours) |
Number of seconds to cache the response data of Google Maps Places API. |
defaultValue |
string | "" |
Default value for the input element. |
It's returned with the following properties.
| Key | Type | Default | Description |
|---|---|---|---|
ready |
boolean | false |
The ready status of usePlacesAutocomplete. |
value |
string | "" |
value for the input element. |
suggestions |
object | { loading: false, status: "", data: [] } |
See suggestions. |
setValue |
function | (value, shouldFetchData = true) => {} |
See setValue. |
clearSuggestions |
function | See clearSuggestions. | |
clearCache |
function | (key = cacheKey) => {} |
Clears the cached data. |
completeSession |
function | () => {} |
Used to complete an autocomplete session once a place's details have been loaded. |
The search result of Google Maps Places API, which contains the following properties:
loading: boolean- indicates the status of a request is pending or has been completed. It's useful for displaying a loading indicator for the user.status: string- indicates the status of the API response, which has these values. It's useful to decide whether we should display the dropdown or not.data: array- an array of suggestion objects each contains all the data.
Set the value of the input element. Use the case below.
import usePlacesAutocomplete from 'use-places-autocomplete';
const PlacesAutocomplete = () => {
const { value, setValue } = usePlacesAutocomplete();
const handleInput = (e) => {
// Place a "string" to update the value of the input element
setValue(e.target.value);
};
return (
<div>
<input value={value} onChange={handleInput} />
{/* Render dropdown */}
</div>
);
};In addition, the setValue method has an extra parameter, which can be used to
disable hitting Google Maps Places API.
import usePlacesAutocomplete from 'use-places-autocomplete';
const PlacesAutocomplete = () => {
const {
value,
suggestions: { status, data },
setValue,
} = usePlacesAutocomplete();
const handleSelect =
({ text }) =>
() => {
// When the user selects a place, we can replace the keyword without requesting data from the API
// by setting the second parameter to "false"
setValue(text.text, false);
};
const renderSuggestions = () =>
data.map((suggestion) => (
<li key={suggestion.placeId} onClick={handleSelect(suggestion)}>
{suggestion.text.text}
</li>
));
return (
<div>
<input value={value} onChange={handleInput} />
{status === 'OK' && <ul>{renderSuggestions()}</ul>}
</div>
);
};Calling the method will clear and reset all the properties of the suggestions
object to default. It's useful for dismissing the dropdown.
import useOnclickOutside from 'react-cool-onclickoutside';
import usePlacesAutocomplete from 'use-places-autocomplete';
const PlacesAutocomplete = () => {
const {
value,
suggestions: { status, data },
setValue,
clearSuggestions,
} = usePlacesAutocomplete();
const ref = useOnclickOutside(() => {
// When the user clicks outside of the component, call it to clear and reset the suggestions data
clearSuggestions();
});
const renderSuggestions = () =>
data.map((suggestion) => (
<li key={suggestion.place_id} onClick={handleSelect(suggestion)}>
{/* Render suggestion text */}
</li>
));
return (
<div ref={ref}>
<input value={value} onChange={handleInput} />
{/* After calling the clearSuggestions(), the "status" is reset so the dropdown is hidden */}
{status === 'OK' && <ul>{renderSuggestions()}</ul>}
</div>
);
};We provide getGeocode and getLatLng, getZipCode utils for you to do geocoding and get geographic coordinates when needed.
It helps you convert address (e.g. "Section 5, Xinyi Road, Xinyi District, Taipei City, Taiwan") into geographic coordinates (e.g. latitude 25.033976 and longitude 121.5645389), or restrict the results to a specific area by Google Maps Geocoding API.
In case you want to restrict the results to a specific area, you will have to
pass the address and the componentRestrictions matching the
GeocoderComponentRestrictions interface.
import { getGeocode } from 'use-places-autocomplete';
const parameter = {
address: 'Section 5, Xinyi Road, Xinyi District, Taipei City, Taiwan',
// or
placeId: 'ChIJraeA2rarQjQRPBBjyR3RxKw',
};
getGeocode(parameter)
.then((results) => {
console.log('Geocoding results: ', results);
})
.catch((error) => {
console.log('Error: ', error);
});getGeocode is an asynchronous function with the following API:
parameter: object- you must supply one, only one ofaddressorlocationorplaceIdand optionallybounds,componentRestrictions,region. It'll be passed as Geocoding Requests.results: array- an array of objects each contains all the data.error: string- the error status of API response, which has these values (except for "OK").
It helps you get the lat and lng from the result object of getGeocode.
import { getGeocode, getLatLng } from 'use-places-autocomplete';
const parameter = {
address: 'Section 5, Xinyi Road, Xinyi District, Taipei City, Taiwan',
};
getGeocode(parameter).then((results) => {
const { lat, lng } = getLatLng(results[0]);
console.log('Coordinates: ', { lat, lng });
});getLatLng is a function with the following API:
parameter: object- the result object ofgetGeocode.latLng: object- contains the latitude and longitude properties.error: any- an exception.
It helps you get the postal_code from the result object of getGeocode.
import { getGeocode, getZipCode } from 'use-places-autocomplete';
const parameter = {
address: 'Section 5, Xinyi Road, Xinyi District, Taipei City, Taiwan',
};
getGeocode(parameter)
// By default we use the "long_name" value from API response, you can tell the utility to use "short_name"
// by setting the second parameter to "true"
.then((results) => {
const zipCode = getZipCode(results[0], false);
console.log('ZIP Code: ', zipCode);
});getZipCode is a function with the following API:
parameters- there're two parameters:1st: object- the result object ofgetGeocode.2nd: boolean- should use theshort_nameor not from API response, default isfalse.
zipCode: string | null- the zip code. If the address doesn't have a zip code it will benull.error: any- an exception.
Retrieves a great deal of information about a particular place ID
(suggestion).
import usePlacesAutocomplete, { getDetails } from 'use-places-autocomplete';
const PlacesAutocomplete = () => {
const { suggestions, value, setValue } = usePlacesAutocomplete();
const handleInput = (e) => {
// Place a "string" to update the value of the input element
setValue(e.target.value);
};
const submit = () => {
const parameter = {
// Use the "place_id" of suggestion from the dropdown (object), here just taking the first suggestion for brevity
placeId: suggestions[0].place_id,
// Specify the return data that you want (optional)
fields: ['name', 'rating'],
};
getDetails(parameter)
.then((details) => {
console.log('Details: ', details);
})
.catch((error) => {
console.log('Error: ', error);
});
};
return (
<div>
<input value={value} onChange={handleInput} />
{/* Render dropdown */}
<button onClick={submit}>Submit Suggestion</button>
</div>
);
};getDetails is an asynchronous function with the following API:
parameter: object- the request of the PlacesService'sgetDetails()method. You must supply theplaceIdthat you would like details about. If you do not specify any fields or omit the fields parameter you will get every field available.placeResult: object | null- the details about the specific place your queried.error: any- an exception.
β οΈ warning, you are billed based on how much information you retrieve, So it is advised that you retrieve just what you need.
π‘ If you have written any blog post or article about
use-places-autocomplete, please open a PR to add it here.
- Featured on React Status #175.
- Featured on React Newsletter #199.
Thanks goes to these wonderful people (emoji key):
This project follows the all-contributors specification. Contributions of any kind are welcome!
