HapplyUI

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-area
  • react-remove-scroll

Registry dependencies

These are automatically installed when you add this component.

  • form-field-context
  • use-form-field-binding
  • popover
  • input
  • happly-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.

PropTypeDefaultDescription
locationLocationRequest-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.
placeholderstring'Search address...'Input placeholder text.
iconReact.ElementType | nullRiMapPinLineLeading icon component. Pass `null` to omit the leading icon — `Multi` uses this to render unadorned rows.
size'medium' | 'small' | 'xsmall''medium'Input size variant.
hasErrorbooleanfalseError state. Automatically read from FormField context if not provided.
disabledbooleanfalseDisabled state. Automatically read from FormField context if not provided.
countryRestrictionsstring[]['ca', 'us', 'fr']ISO 3166-1 alpha-2 country codes for autocomplete restrictions.
excludePlaceIdsstring[]-Place IDs to filter out of the suggestion list. Used by `Multi` to prevent the same address from being picked across rows.
trailingReactNode-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`.

PropTypeDefaultDescription
locationsLocationRequest[]-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).
minLocationsnumber1Minimum visible rows. The remove button is hidden once this floor is reached.
maxLocationsnumber-Maximum number of rows. When reached, the add button is disabled.
addLabelstring'Add another location'Label text for the add-row button.
size'medium' | 'small' | 'xsmall''medium'Input size variant applied to every row.
hasErrorbooleanfalseError state applied to every row. Read from FormField context if not provided.
disabledbooleanfalseDisabled state applied to every row and the add button. Read from FormField context if not provided.
countryRestrictionsstring[]['ca', 'us', 'fr']ISO 3166-1 alpha-2 country codes for autocomplete restrictions, applied to every row.
placeholderstring'Search address...'Placeholder for each row's input.

Previous
Date Input