Skip to content
weftGitHub

PlaceLookupInput

Find a place, like a city or an airport, through your own search.

SourcePackage @weft-js/warp

Preview

tsx
import { PlaceLookupInput } from '@weft-js/warp'import type { Place } from '@weft-js/warp'export function Basic() {  return (    <PlaceLookupInput label="Office location" hint="Try Stockholm or Lima." search={findPlace} biases={PLACE_KINDS} />  )}const PLACES: Place[] = [  { id: 'sto', displayName: 'Stockholm, Sweden', category: 'City', lat: 59.3293, lng: 18.0686, timezone: 'Europe/Stockholm' },  { id: 'ber', displayName: 'Berlin, Germany', category: 'City', lat: 52.52, lng: 13.405, timezone: 'Europe/Berlin' },  { id: 'lis', displayName: 'Lisbon, Portugal', category: 'City', lat: 38.7223, lng: -9.1393, timezone: 'Europe/Lisbon' },  { id: 'lim', displayName: 'Lima, Peru', category: 'City', lat: -12.0464, lng: -77.0428, timezone: 'America/Lima' },  { id: 'tok', displayName: 'Tokyo, Japan', category: 'City', lat: 35.6762, lng: 139.6503, timezone: 'Asia/Tokyo' },  { id: 'arn', displayName: 'Stockholm Arlanda Airport', category: 'Airport', lat: 59.6498, lng: 17.9238, timezone: 'Europe/Stockholm' },  { id: 'lhr', displayName: 'London Heathrow Airport', category: 'Airport', lat: 51.47, lng: -0.4543, timezone: 'Europe/London' },]const PLACE_KINDS = [{ id: 'city', label: 'Cities', default: true }, { id: 'air', label: 'Airports' }]const findPlace = async (q: string, o: { bias: Record<string, boolean> }) => {  await new Promise(done => { setTimeout(done, 200) })  const kinds = [o.bias.city && 'City', o.bias.air && 'Airport']  return PLACES.filter(p => kinds.includes(p.category) && p.displayName.toLowerCase().includes(q.toLowerCase()))}

Installation

npm install @weft-js/warp

Usage

tsx
import { PlaceLookupInput } from '@weft-js/warp'<PlaceLookupInput label="Office location" hint="Try Stockholm or Lima." search={findPlace} biases={PLACE_KINDS} />

Use what was picked

The value is the whole place, coordinates and time zone included.

tsx
import { state } from '@weft-js/core'import { PlaceLookupInput, Stack, Text } from '@weft-js/warp'import type { Place } from '@weft-js/warp'export function Picked() {  const place = state<Place | null>(PLACES[0] ?? null)  return (    <Stack gap="related">      <PlaceLookupInput label="Nearest office" search={findPlace} value={place} />      <Text subtle>{place.value ? `New deploys run close to ${place.value.displayName}.` : 'Pick a place from the list.'}</Text>    </Stack>  )}const PLACES: Place[] = [  { id: 'sto', displayName: 'Stockholm, Sweden', category: 'City', lat: 59.3293, lng: 18.0686, timezone: 'Europe/Stockholm' },  { id: 'ber', displayName: 'Berlin, Germany', category: 'City', lat: 52.52, lng: 13.405, timezone: 'Europe/Berlin' },  { id: 'lis', displayName: 'Lisbon, Portugal', category: 'City', lat: 38.7223, lng: -9.1393, timezone: 'Europe/Lisbon' },  { id: 'lim', displayName: 'Lima, Peru', category: 'City', lat: -12.0464, lng: -77.0428, timezone: 'America/Lima' },  { id: 'tok', displayName: 'Tokyo, Japan', category: 'City', lat: 35.6762, lng: 139.6503, timezone: 'Asia/Tokyo' },  { id: 'arn', displayName: 'Stockholm Arlanda Airport', category: 'Airport', lat: 59.6498, lng: 17.9238, timezone: 'Europe/Stockholm' },  { id: 'lhr', displayName: 'London Heathrow Airport', category: 'Airport', lat: 51.47, lng: -0.4543, timezone: 'Europe/London' },]const findPlace = async (q: string, o: { bias: Record<string, boolean> }) => {  await new Promise(done => { setTimeout(done, 200) })  const kinds = [o.bias.city && 'City', o.bias.air && 'Airport']  return PLACES.filter(p => kinds.includes(p.category) && p.displayName.toLowerCase().includes(q.toLowerCase()))}

States

A place lookup can show a hint or an error, and can be disabled or readonly.

tsx
import { Grid, PlaceLookupInput, Stack, Text } from '@weft-js/warp'import type { Place } from '@weft-js/warp'export function States() {  return (    <Grid cols={3} gap="group">      <Cell name="Empty"><PlaceLookupInput label="Office location" search={findPlace} /></Cell>      <Cell name="Filled"><PlaceLookupInput label="Office location" search={findPlace} defaultValue={PLACES[1]!} /></Cell>      <Cell name="Hint"><PlaceLookupInput label="Office location" search={findPlace} hint="Used to pick a region." /></Cell>      <Cell name="Invalid"><PlaceLookupInput label="Office location" search={findPlace} error="Pick a place from the list." /></Cell>      <Cell name="Disabled"><PlaceLookupInput label="Office location" search={findPlace} defaultValue={PLACES[1]!} disabled /></Cell>      <Cell name="Read-only"><PlaceLookupInput label="Office location" search={findPlace} defaultValue={PLACES[1]!} readonly /></Cell>    </Grid>  )}const Cell = (p: { name: string; children?: unknown }) => (  <Stack gap="tight">    <Text role="caption" subtle>{p.name}</Text>    {p.children}  </Stack>)const PLACES: Place[] = [  { id: 'sto', displayName: 'Stockholm, Sweden', category: 'City', lat: 59.3293, lng: 18.0686, timezone: 'Europe/Stockholm' },  { id: 'ber', displayName: 'Berlin, Germany', category: 'City', lat: 52.52, lng: 13.405, timezone: 'Europe/Berlin' },  { id: 'lis', displayName: 'Lisbon, Portugal', category: 'City', lat: 38.7223, lng: -9.1393, timezone: 'Europe/Lisbon' },  { id: 'lim', displayName: 'Lima, Peru', category: 'City', lat: -12.0464, lng: -77.0428, timezone: 'America/Lima' },  { id: 'tok', displayName: 'Tokyo, Japan', category: 'City', lat: 35.6762, lng: 139.6503, timezone: 'Asia/Tokyo' },  { id: 'arn', displayName: 'Stockholm Arlanda Airport', category: 'Airport', lat: 59.6498, lng: 17.9238, timezone: 'Europe/Stockholm' },  { id: 'lhr', displayName: 'London Heathrow Airport', category: 'Airport', lat: 51.47, lng: -0.4543, timezone: 'Europe/London' },]const findPlace = async (q: string, o: { bias: Record<string, boolean> }) => {  await new Promise(done => { setTimeout(done, 200) })  const kinds = [o.bias.city && 'City', o.bias.air && 'Airport']  return PLACES.filter(p => kinds.includes(p.category) && p.displayName.toLowerCase().includes(q.toLowerCase()))}

When to use

Use it for

  • A location, not somewhere to send a letter

Reach for something else

Accessibility

↑↓
Moves through the results
Enter
Picks the highlighted place
Esc
Closes the list

API Reference

PropTypeDefaultDescription
labelstring—The visible name, and what screen readers announce.
hintunknown—A short line of help under the field.
errorunknown—Replaces the hint and marks the field invalid.
iconIconRef—Leading icon: it sweeps with the border.
prefixunknown—Text or an element inside the field before the value, such as $.
suffixunknown—Text or an element inside the field after the value, such as kg.
variant"floating" | "classic"—floating (default, or the app's warp({ fields })) · classic: the label above.
appearance"line"—line: only a bottom line under the value, no box or fill. The boxed field otherwise.
size"sm" | "md" | "lg"'md'Control size. md by default.
clearablebooleanfalseA clear button (shown once there is a value).
counternumber—Maximum length, shown as a counter.
idstring—The element id. One is made for you if you leave it out.
disabledbooleanfalseCan't be focused or changed.
readonlybooleanfalseShows the value and lets it be selected and copied, but not changed.
requiredbooleanfalseThe form will not submit without it.
namestring—Posts the value with a form.
localestring—The language and region used to format values.
todaystring—Pins "today" (server render, tests); the browser's clock otherwise.
presentationPresentation—auto opens a popover, or a sheet when the viewport is under 600px wide; popover and sheet force one.
valuePlace | State<Place | null> | null—The place, or a State to bind.
defaultValuePlace | null—The starting value, when the component owns the state.
search(q: string, o: { bias: Record<string, boolean>; signal: AbortSignal; }) …—Your geocoder. warp ships none. Gets the text, the filters and an abort signal.
biasesPlaceBias[]—Filter chips: { id, label, default }.
minCharsnumber2How many letters before it searches.
EventTypeDefaultDescription
onInput((e: InputEvent & { currentTarget: HTMLInputElement & HTMLTextAreaElemen…—Called on each keystroke.
onValueChange((v: Place | null, reason: ChangeReason) => void)—Hears the new place and why it changed.

RelatedAddress field