Location Input
A Google Maps address autocomplete input with async search and place resolution. Use `Multi` to collect a list of locations.
Installation
bunx @happlyui/cli@latest add location-input
Dependencies
npm packages
use-debounce@remixicon/react@radix-ui/react-scroll-areareact-remove-scroll
Registry dependencies
These are automatically installed when you add this component.
form-field-contextuse-form-field-bindingpopoverinputhapply-ui-utils
Usage
import * as LocationInput from "@/components/ui/location-input"
// Single
const [location, setLocation] = useState<LocationRequest>(null)
<LocationInput.Root
location={location}
onLocationChange={setLocation}
placeholder="Search address..."
/>
// Multi
const [locations, setLocations] = useState<NonNullable<LocationRequest>[]>([])
<LocationInput.Multi
locations={locations}
onLocationsChange={setLocations}
/>
Examples
Default
Basic location input with default settings.
const [location, setLocation] = useState<LocationRequest>(null)
<LocationInput.Root
location={location}
onLocationChange={setLocation}
/>
With Error State
Manually set error state.
<LocationInput.Root
location={location}
onLocationChange={setLocation}
hasError
/>
Multi
Collect an ordered list of locations. Each row is an autocomplete input; rows can be added with the trailing button and removed with the inline minus icon (shown when more than `minLocations` rows are visible).
const [locations, setLocations] = useState<NonNullable<LocationRequest>[]>([])
<LocationInput.Multi
locations={locations}
onLocationsChange={setLocations}
/>
Multi with Max
Cap the number of locations. The add button is disabled once `maxLocations` is reached.
<LocationInput.Multi
locations={locations}
onLocationsChange={setLocations}
maxLocations={3}
addLabel="Add location (max 3)"
/>
With React Hook Form
Used inside a FormField.Root with automatic error handling and validation on blur. Both `Root` and `Multi` auto-bind to RHF: `Root` stores a single `LocationRequest`, `Multi` stores `LocationRequest[]`.
<FormField.Root name="location" label="Location" required>
<LocationInput.Root />
</FormField.Root>
<FormField.Root name="locations" label="Locations">
<LocationInput.Multi />
</FormField.Root>
Custom Country Restrictions
Restrict autocomplete to specific countries.
<LocationInput.Root
location={location}
onLocationChange={setLocation}
countryRestrictions={['ca', 'us']}
/>
API Reference
LocationInput.Root
Google Maps address autocomplete input. Reads `hasError` and `onBlur` from FormField context when available.
| Prop | Type | Default | Description |
|---|---|---|---|
location | LocationRequest | - | The current location object. Pass `null` for empty state. The component syncs its internal search text when this prop changes (e.g. on form reset). |
onLocationChange | (location: LocationRequest) => void | - | Callback when a location is selected or cleared. |
placeholder | string | 'Search address...' | Input placeholder text. |
icon | React.ElementType | null | RiMapPinLine | Leading icon component. Pass `null` to omit the leading icon — `Multi` uses this to render unadorned rows. |
size | 'medium' | 'small' | 'xsmall' | 'medium' | Input size variant. |
hasError | boolean | false | Error state. Automatically read from FormField context if not provided. |
disabled | boolean | false | Disabled state. Automatically read from FormField context if not provided. |
countryRestrictions | string[] | ['ca', 'us', 'fr'] | ISO 3166-1 alpha-2 country codes for autocomplete restrictions. |
excludePlaceIds | string[] | - | Place IDs to filter out of the suggestion list. Used by `Multi` to prevent the same address from being picked across rows. |
trailing | ReactNode | - | Optional slot rendered inside the input wrapper after the input. Used by `Multi` to inline a per-row remove button; can also hold custom adornments. |
LocationInput.Multi
Collects a list of locations. Renders one `Root` per row with an inline remove button, plus an add-row button below. Prevents duplicate selections by filtering each row's suggestion list against the place IDs already picked in other rows, and disables the add button until every existing row has a selected place. Auto-binds to React Hook Form as `LocationRequest[]` when inside a `FormField.Root`.
| Prop | Type | Default | Description |
|---|---|---|---|
locations | LocationRequest[] | - | The list of selected locations. Optional — auto-binds to RHF when inside `FormField.Root`. |
onLocationsChange | (locations: LocationRequest[]) => void | - | Callback when the list changes (rows added, removed, or a row's place is selected/changed). |
minLocations | number | 1 | Minimum visible rows. The remove button is hidden once this floor is reached. |
maxLocations | number | - | Maximum number of rows. When reached, the add button is disabled. |
addLabel | string | 'Add another location' | Label text for the add-row button. |
size | 'medium' | 'small' | 'xsmall' | 'medium' | Input size variant applied to every row. |
hasError | boolean | false | Error state applied to every row. Read from FormField context if not provided. |
disabled | boolean | false | Disabled state applied to every row and the add button. Read from FormField context if not provided. |
countryRestrictions | string[] | ['ca', 'us', 'fr'] | ISO 3166-1 alpha-2 country codes for autocomplete restrictions, applied to every row. |
placeholder | string | 'Search address...' | Placeholder for each row's input. |