
SearchPlaces | MapView |
|---|---|---|
| Powered by | Google Maps JavaScript API (@react-google-maps/api) | Mapbox GL (mapbox-gl) |
| Requires | PlacesProvider ancestor | Nothing — self-contained |
| Credential | Google Maps API key, with the Places library enabled | Mapbox GL public access token (pk.…) |
| What it does | Autocomplete text input → structured address + coordinates | Renders an interactive map with click-able markers |bash
bun add @react-google-maps/api mapbox-gl
SearchPlaces only renders once its PlacesProvider ancestor reports the script has finished loading, via a render-prop:tsx
import { PlacesProvider } from "@arkyn/components/placesProvider";import { SearchPlaces } from "@arkyn/components/searchPlaces";<PlacesProvider apiKey={env.GOOGLE_MAPS_API_KEY}>{(isLoaded) =>isLoaded ? (<SearchPlaces name="address" label="Address" />) : (<span>Loading…</span>)}</PlacesProvider>
SearchPlaces's onPlaceChanged callback returns a structured PlaceData object, including coordinates: { lat, lng } — exactly the shape MapView's coordinates prop expects for a single marker.tsx
import { useState } from "react";import { PlacesProvider } from "@arkyn/components/placesProvider";import { SearchPlaces } from "@arkyn/components/searchPlaces";import { MapView } from "@arkyn/components/mapView";function AddressPicker() {const [coordinates, setCoordinates] = useState<{ lat: number; lng: number }>();const [address, setAddress] = useState<string>();return (<div><PlacesProvider apiKey={env.GOOGLE_MAPS_API_KEY}>{(isLoaded) =>isLoaded ? (<SearchPlacesname="address"label="Address"onPlaceChanged={(place) => {setAddress(`${place.street}, ${place.streetNumber} - ${place.city}`);setCoordinates(place.coordinates);}}/>) : (<span>Loading…</span>)}</PlacesProvider><MapViewaccessToken={env.MAPBOX_ACCESS_TOKEN}zoom={15}coordinates={coordinates ? [{ ...coordinates, popUp: <p>{address}</p> }] : []}/></div>);}
MapView renders a placeholder icon instead of a map whenever coordinates is empty — which is exactly the state you're in before the user has picked a place, so there's no extra empty-state handling to write.componentRestrictionsoptions={{ componentRestrictions: { country: "br" } }}
to SearchPlaces to keep autocomplete suggestions relevant — see the options shape.onMarkerClick receives the data payload you attached to each coordinate — useful for opening a details panel or a drawer without a second round-trip:tsx
<MapViewaccessToken={env.MAPBOX_ACCESS_TOKEN}coordinates={stores.map((store) => ({lat: store.lat,lng: store.lng,data: { id: store.id },popUp: <p>{store.name}</p>,}))}onMarkerClick={(coordinate) => openDrawer("store", { id: coordinate.data.id })}/>
SearchPlaces is a thin wrapper over Input, so every other Input prop (variant, size, errorMessage, form-context fallback, etc.) works the same way — see Build forms with server-side validation if you're validating the submitted address server-side.PlaceData's stateShortName field lines up with the value field of @arkyn/templates' brazilianStates — see Localize inputs with templates if you need to reconcile a free-text address search with a fixed state selector elsewhere in the same form.MapView renders client-side only and shows the same placeholder before hydration as it does with no coordinates — don't treat a visible placeholder as a sign something is broken during SSR.