Add Custom Pin Components | Yext Hitchhikers Platform
What You’ll Learn
In this section, you will:
- Create custom map pins for displaying locations on the map
Overview
In this unit, you will create a custom map pin that will match the style of your site and display the address of the location in a popup when you click on it:
In your new component, you’re going to be using some Mapbox GL types. Import them with the following command:
npm install --save @types/mapbox-gl
1. Create the Custom Pin Component
In src/components
, create a new file called MapPin.tsx
. Add the following code:
import * as ReactDOM from "react-dom/server";
import { PinComponent } from "@yext/search-ui-react";
import mapboxgl from "mapbox-gl";
import Location, { Coordinate } from "../types/locations";
import { useCallback, useEffect, useRef, useState } from "react";
import { GiTacos } from "react-icons/gi";
import { Result } from "@yext/search-headless-react";
const transformToMapboxCoord = (
coordinate: Coordinate
): LngLatLike | undefined => {
if (!coordinate.latitude || !coordinate.longitude) return;
return {
lng: coordinate.longitude,
lat: coordinate.latitude,
};
};
const getLocationHTML = (location: Location) => {
const address = location.address;
const html = (
<div>
<p className="font-bold">{location.neighborhood || "unknown location"}</p>
<p>{location.address.line1}</p>
<p>{`${address.city}, ${address.region}, ${address.postalCode}`}</p>
</div>
);
return ReactDOM.renderToString(html);
};
const MapPin: PinComponent<Location> = ({
index,
mapbox,
result,
}: {
index: number;
mapbox: Map;
result: Result<Location>;
}) => {
const location = result.rawData;
const [active, setActive] = useState(false);
const popupRef = useRef(
new Popup({ offset: 15 }).on("close", () => setActive(false))
);
useEffect(() => {
if (active && location.yextDisplayCoordinate) {
const mapboxCoordinate = transformToMapboxCoord(
location.yextDisplayCoordinate
);
if (mapboxCoordinate) {
popupRef.current
.setLngLat(mapboxCoordinate)
.setHTML(getLocationHTML(location))
.addTo(mapbox);
}
}
}, [active, mapbox, location]);
const handleClick = useCallback(() => {
setActive(true);
}, []);
return (
<button onClick={handleClick}>
<GiTacos className="text-orange" size={30} />
</button>
);
};
export default MapPin;
Let’s review what you just added:
MapPin
returns a button shaped like an orange taco. When the button is clicked, a popup will appear next to the pin with theneighborhood
andaddress
of the the location it represents.- The
Popup
class you imported frommapbox-gl
has a method calledsetHTML
that accepts a string parameter.getLocationHTML
is used to generate a HTML string for the popup.
2. Pass MapPin as the PinComponent Prop
Now that your custom pin is ready, it can be used by the MapboxMap
component to render a pin for each set of location coordinates.
In StoreLocator
, update your imports to include the MapPin
:
// src/components/StoreLocator.tsx
import {
MapboxMap,
FilterSearch,
OnSelectParams,
VerticalResults,
} from "@yext/search-ui-react";
import {
Matcher,
SelectableStaticFilter,
useSearchActions,
} from "@yext/search-headless-react";
// Mapbox CSS bundle
import "mapbox-gl/dist/mapbox-gl.css";
import LocationCard from "./LocationCard";
import MapPin from "./MapPin"; // New
Then, pass MapPin
as the PinComponent
prop on MapboxMap
:
// src/components/StoreLocator.tsx
// imports...
const StoreLocator = (): JSX.Element => {
// ...other code from previous units
return (
<>
<div className="flex h-[calc(100vh-210px)] border">
<div className="w-1/3 flex flex-col">
<FilterSearch
onSelect={handleFilterSelect}
placeholder="Find Locations Near You"
searchFields={[
{
entityType: "location",
fieldApiName: "builtin.location",
},
]}
/>
<VerticalResults
customCssClasses={{ verticalResultsContainer: "overflow-y-auto" }}
CardComponent={LocationCard}
/>
</div>
<div className="w-2/3">
<MapboxMap
mapboxAccessToken={YEXT_PUBLIC_MAPBOX_API_KEY || ""}
PinComponent={MapPin} // New
/>
</div>
</div>
</>
);
};
If your site isn’t already running, start it back up with npm run dev
and go the locator page. Search for “New York City, New York, NY” to see your new map pins.