Add directions to React
Two addresses in, one line on the map, plus the distance and duration to label it with. The copied panel is the whole interaction, including geocoding both endpoints. The hook is the routing call on its own, for when the from and to already exist somewhere in your app.
Install
npx @unmap/cli add routing-panelnpm i @unmap/reactThe panel
Like the geocoder, <RoutingPanel> is a child of <Map>: it draws onto the map it finds through
map-context, and it resolves both text inputs to coordinates for you.
import { Map } from "@/components/ui/map";
import { RoutingPanel } from "@/components/ui/routing-panel";
export default function App() {
const apiKey = import.meta.env.VITE_UNMAP_KEY as string;
return (
<div className="h-dvh">
<Map apiKey={apiKey} center={[-114.07, 51.05]} zoom={11} className="h-full">
<div className="absolute left-3 top-3 z-10 w-80">
<RoutingPanel apiKey={apiKey} mode="auto" />
</div>
</Map>
</div>
);
}import { useEffect } from "react";
import { useDirections, useUnmap } from "@unmap/react";
export default function Leg() {
const apiKey = import.meta.env.VITE_UNMAP_KEY as string;
const { mapContainer, map, ready } = useUnmap({ apiKey, center: [-114.07, 51.05], zoom: 11 });
const { route } = useDirections({ apiKey });
useEffect(() => {
if (!ready || !map) return;
void route([-114.07, 51.05], [-113.99, 51.04], { mode: "auto" }).then((leg) => {
map.addSource("leg", {
type: "geojson",
data: { type: "Feature", properties: {}, geometry: leg.geometry },
});
map.addLayer({
id: "leg-line",
type: "line",
source: "leg",
layout: { "line-cap": "round", "line-join": "round" },
paint: { "line-color": "#FF3E9A", "line-width": 3 },
});
});
}, [ready, map, route]);
return <div ref={mapContainer} style={{ height: "100dvh" }} />;
}Wait for ready before adding the source. A map that has not fired load has no style yet, and
addSource on it throws.
Coordinates, not addresses
route takes [lng, lat] pairs. If your inputs are text, geocode them first, which is one extra
call each and the reason the copied panel exists:
import { useGeocoder, useDirections } from "@unmap/react";
export default function RouteBetween() {
const apiKey = import.meta.env.VITE_UNMAP_KEY as string;
const { search } = useGeocoder({ apiKey });
const { route } = useDirections({ apiKey });
async function go(fromText: string, toText: string) {
const [from] = await search(fromText, { limit: 1 });
const [to] = await search(toText, { limit: 1 });
if (!from || !to) throw new Error("could not resolve both endpoints");
return route([from.lng, from.lat], [to.lng, to.lat], { mode: "auto" });
}
return <button onClick={() => void go("Calgary Tower", "Bow Valley Square")}>Route</button>;
}What comes back
distanceMeters, durationSeconds, and a GeoJSON LineString. That is the whole result, so the
label beside your line needs no second call. Five travel modes are available: auto, car,
bicycle, pedestrian and truck, and an unknown one is a 400 rather than a silent car route.
The duration is a model of the road network with no live traffic in it. It is a good estimate of a quiet Sunday and an optimistic one of Friday at five.
Common problems
addSource throws. The map has not loaded. Gate on ready.
The route stops at the border. The graph is the Canada extract, so there is no cross-border routing. A pair of endpoints either side of the line has no answer rather than a detour.
A truck route is refused. mode: "truck" needs a valid vehicle profile; the
routing docs list the dimensions and their limits.
Related
- Get directions for the framework-free version
- Travel modes for the five modes side by side
- Add a map to React for the map this draws on