Tutorial: Implementing a Google Maps Autocomplete Component with Chakra UI and React Hook Form
In this tutorial, we will create a Google Maps Autocomplete component using Chakra UI and React Hook Form. This component will enable users to search for and select locations, enhancing the user interface with autocomplete suggestions.
Prerequisites
Before you start, ensure you have the following installed:
- Node.js
- npm or yarn
- A Google Cloud project with the Places API enabled
Step 1: Setting Up Your Project
First, create a new React project if you haven’t already:
npx create-react-app location-search
cd location-search
Next, install the necessary dependencies:
npm install @chakra-ui/react @chakra-ui/icons @emotion/react @emotion/styled framer-motion react-hook-form lodash react-google-places-autocomplete chakra-react-select
Step 2: Setting Up Chakra UI
Wrap your application with the ChakraProvider
to use Chakra UI components:
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { ChakraProvider } from '@chakra-ui/react';
import App from './App';
ReactDOM.render(
<ChakraProvider>
<App />
</ChakraProvider>,
document.getElementById('root')
);
Step 3: Creating the LocationSearchSelect Component
Create a new file LocationSearchSelect.js
in the src
directory:
// src/LocationSearchSelect.js
import React, { useState, useEffect } from 'react';
import { Flex, FormControl, FormErrorMessage, FormLabel, IconButton, InputGroup, chakra, useColorMode } from '@chakra-ui/react';
import { Controller } from 'react-hook-form';
import { Select } from 'chakra-react-select';
import { geocodeByAddress, getLatLng } from 'react-google-places-autocomplete';
import { debounce } from 'lodash';
const LocationSearchSelect = ({ label, placeholder, errors, id, control, required = false, rules, containerStyles, onRefresh, inputProps, isLoading, additionalObj, size, leftAddon, hideLabel, labelProps }) => {
const { colorMode } = useColorMode();
if (required) {
required = `${label} is required`;
}
const [autocompleteService, setAutocompleteService] = useState(null);
const [predictions, setPredictions] = useState([]);
useEffect(() => {
const autocomplete = new window.google.maps.places.AutocompleteService();
setAutocompleteService(autocomplete);
}, []);
const handleSearch = debounce((value) => {
if (autocompleteService) {
autocompleteService.getPlacePredictions(
{ input: value },
(predictions, status) => {
if (status === window.google.maps.places.PlacesServiceStatus.OK) {
setPredictions(predictions);
} else {
setPredictions([]);
}
}
);
}
}, 300);
return (
<Controller
control={control}
name={id}
rules={{
required: required,
...rules
}}
render={({ field: { onChange, onBlur, value, ref, ...rest } }) => {
return (
<Flex align="end" w="full">
<FormControl isInvalid={errors[id]} {...containerStyles}>
{!hideLabel && (
<FormLabel htmlFor={id} fontSize={'13px'} {...labelProps}>
{label}
{required && <chakra.span color="red.500">*</chakra.span>}
</FormLabel>
)}
<InputGroup size={size} w="full">
{leftAddon}
<Select
isLoading={isLoading}
onInputChange={handleSearch}
isClearable={false}
onChange={async (selectedOption) => {
if (selectedOption) {
const results = await geocodeByAddress(selectedOption.label);
const latLng = await getLatLng(results[0]);
let updatedValue = { ...selectedOption, latLng };
if (additionalObj) {
updatedValue = { ...updatedValue, ...additionalObj };
}
onChange(updatedValue);
}
}}
value={value}
ref={ref}
placeholder={placeholder}
options={predictions.map((prediction) => ({
value: prediction.place_id,
label: prediction.description,
}))}
menuPosition="fixed"
classNamePrefix="location-select"
id={id}
{...rest}
{...inputProps}
/>
</InputGroup>
<FormErrorMessage>{errors[id] && errors[id].message}</FormErrorMessage>
</FormControl>
{onRefresh && (
<IconButton onClick={onRefresh} ml="1em" aria-label="Refresh" icon={<Icon boxSize={7} as={APP_ICONS.REFRESH} />} />
)}
</Flex>
);
}}
/>
);
};
export default LocationSearchSelect;
Step 4: Adding the Google Maps Script
Add the Google Maps script to your public/index.html
file:
<!DOCTYPE html>
<html lang="en">
<head>
<!-- other head elements -->
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places"></script>
</head>
<body>
<div id="root"></div>
</body>
</html>
Replace YOUR_API_KEY
with your actual Google Maps API key.
Step 5: Using the Component in Your Form
Now, you can use the LocationSearchSelect
component within a form managed by React Hook Form. Here’s an example:
// src/App.js
import React from 'react';
import { useForm, Controller } from 'react-hook-form';
import { Box, Button } from '@chakra-ui/react';
import LocationSearchSelect from './LocationSearchSelect';
const App = () => {
const { handleSubmit, control, formState: { errors } } = useForm();
const onSubmit = (data) => {
console.log(data);
};
return (
<Box p={5}>
<form onSubmit={handleSubmit(onSubmit)}>
<LocationSearchSelect
label="Location"
placeholder="Search for a location"
errors={errors}
id="location"
control={control}
required
/>
<Button mt={4} colorScheme="teal" type="submit">Submit</Button>
</form>
</Box>
);
};
export default App;
Conclusion
You've successfully implemented a Google Maps Autocomplete component using Chakra UI and React Hook Form. This setup provides a robust and user-friendly interface for selecting locations, leveraging the power of Google Places API and the flexibility of React Hook Form.
Find me on your favorite platform
- Github — Follow me on GitHub for further useful code snippets and open source repos.
- LinkedIn Profile — Connect with me on LinkedIn for further discussions and updates.
- Twitter (X) — Connect with me on Twitter (X) for useless tech tweets.