Google provides robust access to its Maps API, which can be leveraged for various location-based functionalities and map-related tasks. In this article, I will explain how we utilized the Google AutocompleteService API to build a Smart Address Search field that replaces a full address form.
By using this approach, we reduced user input errors and improved the user experience by simplifying the address entry process, making it quicker and more accurate (1 smart input vs 3, huh).
First of all, to work with the Maps API you will need to enable it in Google Maps Platform and get the API key.
In our project, we use @react-google-maps/api package (npm i @react-google-maps/api).
Let's initialize the SDK with the custom useGoogleMapsApi hook, to make it reusable within the application:
const useGoogleMapsApi = () => { const api = useJsApiLoader({ id: "app", googleMapsApiKey: "", // set your Public key }); return { ...api, }; };
Places Autocomplete Service provides an API to search within the places. It provides 2 methods:
To implement our search, we used the getPlacePredictions() method. Let's add this method to our code and return it from the hook.
// Function to search for places using Google Autocomplete Service const searchPlaces = async ( query: string, options?: Omit<google.maps.places.AutocompletionRequest, "input"> ): Promise<Array<google.maps.places.AutocompletePrediction>> => { // Load AutocompleteService from Google Maps API const { AutocompleteService } = (await google.maps.importLibrary( "places" )) as google.maps.PlacesLibrary; const autocomplete = new AutocompleteService(); try { // Fetch place predictions based on user query const { predictions } = await autocomplete.getPlacePredictions({ ...options, // Optional additional parameters for more precise searches input: query, // User's search query (e.g., "Baker Street") }); // Return the list of predictions to display to the user return predictions; } catch { // If there's an error, return an empty array return []; } };
AutocompletionRequest interface uses the input field as a search query and other options, which allows to make a search more accurate. We will talk about them later.
We will use a simple input and a list of results for our demo.
import useGoogleMapsApi from "./useGoogleMapsApi"; import { useEffect, useState } from "react"; export default function App() { const { searchPlaces } = useGoogleMapsApi(); // State to store input value from the user const [inputValue, setInputValue] = useState<string>(""); // State to store the list of place predictions from Google Autocomplete const [places, setPlaces] = useState< Array<google.maps.places.AutocompletePrediction> >([]); // Function to handle the search process when the user types a query const handleSearch = async (searchQuery: string) => { const result = await searchPlaces(searchQuery); setPlaces(result); }; // Trigger the search whenever the input value changes useEffect(() => { handleSearch(inputValue); }, [inputValue]); return ( <div style={{ maxWidth: "80%", width: "100%",fontFamily: "sans-serif",}}> <input value={inputValue} onChange={(e) => setInputValue(e.target.value)} placeholder="Find an address" /> {places.map((place) => ( <div style={{ marginBottom: "0.5rem",}}> <span style={{ color: "blue", cursor: "pointer",}}> {place.description} </span> <span style={{ color: "#333", fontSize: "0.75rem",}}>{`(${place.place_id})`}</span> <span> - {place.types.join(", ")}</span> </div> ))} </div> ); }
From the predictions, we are interested in the 3 fields:
AutocompleteService interface has a bunch of inputs, that allow make a search more accurate.
Field componentRestrictions allows us to narrow the search results to one specific country. It supports up to 5 countries and requires country codes to be in ISO 3166-1 Alpha-2 standard. Here you can find a list of countries' codes.
const { predictions } = await autocomplete.getPlacePredictions({ ...options, input: query, componentRestrictions: { country: ["gb"], }, });
To make our field not confusing for the users, we need to exclude places like parks, airports, etc from the search results. The Autocomplete service has a field types to specify the types of predictions to be returned. But only one type is allowed for the AutocompleteService.
const { predictions } = await autocomplete.getPlacePredictions({ ...options, input: query, componentRestrictions: { country: ["gb"], }, types: ["geocode"], });
But it also includes the bigger administrative entities like cities or streets. We need users to select accurate addresses.
Gotcha! That is what we need, right?... Basically, yes. However, it does not allow users to search by postcode, which is a common case for us.
So to achieve precise search results by both street names and postcodes, we wrote a custom filter. Thanks to StackOverflow for the inspiration.
export const filterPredictions = ( results: Array<google.maps.places.AutocompletePrediction> ): Array<google.maps.places.AutocompletePrediction> => results.filter(({ types }) => { // Keep predictions that are street addresses or postal codes if (types.includes("street_address") || types.includes("postal_code")) { return true; } // For geocode types, check for building numbers (premise/subpremise) if (types.includes("geocode")) { return types.some((type) => ["premise", "subpremise"].includes(type)); } return false; // Filter out irrelevant types });
If the result includes the street_address or postal_code, we assume it as the correct result.
If it has the geocode type, we check for the premise or subpremise (shortly saying, the building number or name). More about types you can read here.
The result we achieved:
AutocompleteService returns only search predictions, but not the place details we need. However, with the place id and Geocoder we can get details like exact address, country, postal code and coordinates.
Geocoder was initially created to make conversions between addresses and coordinates, but it completely covers our needs.
If you need to have additional information about the place like reviews and comments, you can use the Places API.
Let's add a new method to our hook:
// Function to get detailed information about a place using its place id const getPlaceById = async ( placeId: string // The place_id from the AutocompleteService ): Promise<google.maps.GeocoderResult | null> => { const geocoder = new google.maps.Geocoder(); // Create a new instance of Geocoder try { const { results } = await geocoder.geocode({ placeId }); // Return the first result return results[0]; } catch { // In case of error, return null return null; } };
To unify the results between counties, where different administrative levels and entities exist, Google uses the address components structure. Let's check an example:
To format the address, we need the Baker Street 221B, NW1 6XE, London (street_number route, postal_code, locality). But, in some cases, the keys structure might differ. To cover it we made an unified serializer:
// Helper function to extract a specific address component by its type (e.g., street_address, postal_code) export const pickAddressComponentByType = ( result: google.maps.GeocoderResult, type: Array<string> ): string => result.address_components.find((component) => component.types.some((componentType) => type.includes(componentType)) )?.long_name || ""; // Function to serialize the geocoded result into a structured format export const serializeGeocoderResult = ( result: google.maps.GeocoderResult ): TFormatedGeocoderResult => ({ formattedAddress: result.formatted_address, streetAddress: pickAddressComponentByType(result, [ "street_address", "premise", "route", ]), streetNumber: pickAddressComponentByType(result, ["street_number"]), city: pickAddressComponentByType(result, [ "locality", "postal_town", "administrative_area_level_2", ]), country: pickAddressComponentByType(result, ["country"]), state: pickAddressComponentByType(result, ["administrative_area_level_1"]), postalCode: pickAddressComponentByType(result, ["postal_code"]), latitude: result.geometry.location.lat(), longitude: result.geometry.location.lng(), });
Notice, that we verified it only for Great Britain. So maybe you might need to enhance it for your specific cases.
In this article, I have shown how we can use different Google Maps API's to build address fields, that can replace full address forms and drastically reduce the time users need to spend on it, reducing the number of mistakes on the users' side.
Check out the DEMO sandbox, but don’t forget to set your own GOOGLE_MAPS_API_KEY.
Feel free to send your questions and suggestions in comments. I'll be glad for any conversations??.
The above is the detailed content of Creating a Smart Address Search with Google Maps API and React. For more information, please follow other related articles on the PHP Chinese website!