arkynChangelogGuides
arkyn
docs / guides / how-to-integrate-places-and-maps

Integrate address search and maps

A common flow — let a user type an address, autocomplete it, then show a pin on a map — actually spans two unrelated map SDKs in Arkyn: address search is built on Google Maps, and map rendering is built on Mapbox. Nothing forces you to use both together, but combining them is the most common real-world use case, and each half needs its own API key.

Two SDKs, two keys

Don't reuse one key for both
A Google Maps API key and a Mapbox access token are not interchangeable — each is issued and billed by its own provider. Keep them as two separate environment variables from the start.
| | 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 |
Install both peer dependencies up front if you're building this combined flow:

bash

bun add @react-google-maps/api mapbox-gl

1. Load the Google Maps script

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>

2. Turn the selected place into a marker

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 ? (
<SearchPlaces
name="address"
label="Address"
onPlaceChanged={(place) => {
setAddress(`${place.street}, ${place.streetNumber} - ${place.city}`);
setCoordinates(place.coordinates);
}}
/>
) : (
<span>Loading…</span>
)
}
</PlacesProvider>
<MapView
accessToken={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.
Restricting componentRestrictions
If your app only serves one country, pass options={{ componentRestrictions: { country: "br" } }} to SearchPlaces to keep autocomplete suggestions relevant — see the
SearchPlaces reference
for the full options shape.

3. Reacting to marker clicks

If you later render multiple coordinates (e.g. a list of stores), 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

<MapView
accessToken={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 })}
/>

Notes

  • 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.
  • Both keys are used client-side and will be visible in the browser; restrict each one by HTTP referrer/domain in its respective provider dashboard (Google Cloud Console, Mapbox account settings) rather than trying to keep them secret.
  • 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.
Related in @arkyn/components
On this page