This example demonstrates how to create a custom legend to help users understand the information presented on your map.
Read the documentation.
TypeScript
const parkingSvg = `<svg fill="white" xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M13 3H6v18h4v-6h3c3.31 0 6-2.69 6-6s-2.69-6-6-6zm.2 8H10V7h3.2c1.1 0 2 .9 2 2s-.9 2-2 2z"/></svg>`; const librarySvg = `<svg fill="white" xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 11.55C9.64 9.35 6.48 8 3 8v11c3.48 0 6.64 1.35 9 3.55 2.36-2.19 5.52-3.55 9-3.55V8c-3.48 0-6.64 1.35-9 3.55zM12 8c1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3 1.34 3 3 3z"/></svg>`; const infoSvg = `<svg fill="white" xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/></svg>`; const icons: Record<string, { name: string; icon: string; color: string }> = { parking: { name: 'Parking', icon: parkingSvg, color: '#1E88E5', // Blue }, library: { name: 'Library', icon: librarySvg, color: '#43A047', // Green }, info: { name: 'Info', icon: infoSvg, color: '#E53935', // Red }, }; async function init(): Promise<void> { const [{ AdvancedMarkerElement, PinElement }, { event }] = await Promise.all([ google.maps.importLibrary('marker'), google.maps.importLibrary('core'), google.maps.importLibrary('maps'), ]); const mapElement = document.querySelector('gmp-map')!; const innerMap = mapElement.innerMap; // Wait for the map to load before building the legend. event.addListenerOnce(innerMap, 'idle', () => { void makeLegend(); }); const features = [ { position: { lat: -33.91721, lng: 151.2263 }, type: 'info', }, { position: { lat: -33.91539, lng: 151.2282 }, type: 'info', }, { position: { lat: -33.91747, lng: 151.22912 }, type: 'info', }, { position: { lat: -33.9191, lng: 151.22907 }, type: 'info', }, { position: { lat: -33.91725, lng: 151.23011 }, type: 'info', }, { position: { lat: -33.91872, lng: 151.23089 }, type: 'info', }, { position: { lat: -33.91784, lng: 151.23094 }, type: 'info', }, { position: { lat: -33.91682, lng: 151.23149 }, type: 'info', }, { position: { lat: -33.9179, lng: 151.23463 }, type: 'info', }, { position: { lat: -33.91666, lng: 151.23468 }, type: 'info', }, { position: { lat: -33.916988, lng: 151.23364 }, type: 'info', }, { position: { lat: -33.91662347903106, lng: 151.22879464019775 }, type: 'parking', }, { position: { lat: -33.916365282092855, lng: 151.22937399734496 }, type: 'parking', }, { position: { lat: -33.91665018901448, lng: 151.2282474695587 }, type: 'parking', }, { position: { lat: -33.919543720969806, lng: 151.23112279762267 }, type: 'parking', }, { position: { lat: -33.91608037421864, lng: 151.23288232673644 }, type: 'parking', }, { position: { lat: -33.91851096391805, lng: 151.2344058214569 }, type: 'parking', }, { position: { lat: -33.91818154739766, lng: 151.2346203981781 }, type: 'parking', }, { position: { lat: -33.91727341958453, lng: 151.23348314155578 }, type: 'library', }, ]; for (const feature of features) { const iconData = icons[feature.type]; const pin = new PinElement({ background: iconData.color, borderColor: iconData.color, glyphSrc: new URL( `data:image/svg+xml;charset=UTF-8,${encodeURIComponent(iconData.icon)}` ), scale: 1.5, }); const marker = new AdvancedMarkerElement({ position: feature.position, title: iconData.name, }); marker.append(pin); mapElement.append(marker); } } async function makeLegend() { const { PinElement } = await google.maps.importLibrary('marker'); const legend = document.getElementById('legend')!; const title = document.createElement('div'); title.innerText = 'Legend'; title.classList.add('title'); legend.appendChild(title); for (const type of Object.values(icons)) { const wrapper = document.createElement('div'); wrapper.classList.add('legend-item'); const pin = new PinElement({ background: type.color, borderColor: type.color, glyphSrc: new URL( `data:image/svg+xml;charset=UTF-8,${encodeURIComponent(type.icon)}` ), scale: 1.0, }); // PinElement extends HTMLElement directly, so we can append it directly! const pinContainer = document.createElement('div'); pinContainer.classList.add('legend-icon'); pinContainer.appendChild(pin); const txt = document.createElement('div'); txt.classList.add('legend-text'); txt.innerText = type.name; wrapper.appendChild(pinContainer); wrapper.appendChild(txt); legend.appendChild(wrapper); } } void init();
JavaScript
const parkingSvg = `<svg fill="white" xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M13 3H6v18h4v-6h3c3.31 0 6-2.69 6-6s-2.69-6-6-6zm.2 8H10V7h3.2c1.1 0 2 .9 2 2s-.9 2-2 2z"/></svg>`; const librarySvg = `<svg fill="white" xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 11.55C9.64 9.35 6.48 8 3 8v11c3.48 0 6.64 1.35 9 3.55 2.36-2.19 5.52-3.55 9-3.55V8c-3.48 0-6.64 1.35-9 3.55zM12 8c1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3 1.34 3 3 3z"/></svg>`; const infoSvg = `<svg fill="white" xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/></svg>`; const icons = { parking: { name: 'Parking', icon: parkingSvg, color: '#1E88E5', // Blue }, library: { name: 'Library', icon: librarySvg, color: '#43A047', // Green }, info: { name: 'Info', icon: infoSvg, color: '#E53935', // Red }, }; async function init() { const [{ AdvancedMarkerElement, PinElement }, { event }] = await Promise.all([ google.maps.importLibrary('marker'), google.maps.importLibrary('core'), google.maps.importLibrary('maps'), ]); const mapElement = document.querySelector('gmp-map'); const innerMap = mapElement.innerMap; // Wait for the map to load before building the legend. event.addListenerOnce(innerMap, 'idle', () => { void makeLegend(); }); const features = [ { position: { lat: -33.91721, lng: 151.2263 }, type: 'info', }, { position: { lat: -33.91539, lng: 151.2282 }, type: 'info', }, { position: { lat: -33.91747, lng: 151.22912 }, type: 'info', }, { position: { lat: -33.9191, lng: 151.22907 }, type: 'info', }, { position: { lat: -33.91725, lng: 151.23011 }, type: 'info', }, { position: { lat: -33.91872, lng: 151.23089 }, type: 'info', }, { position: { lat: -33.91784, lng: 151.23094 }, type: 'info', }, { position: { lat: -33.91682, lng: 151.23149 }, type: 'info', }, { position: { lat: -33.9179, lng: 151.23463 }, type: 'info', }, { position: { lat: -33.91666, lng: 151.23468 }, type: 'info', }, { position: { lat: -33.916988, lng: 151.23364 }, type: 'info', }, { position: { lat: -33.91662347903106, lng: 151.22879464019775 }, type: 'parking', }, { position: { lat: -33.916365282092855, lng: 151.22937399734496 }, type: 'parking', }, { position: { lat: -33.91665018901448, lng: 151.2282474695587 }, type: 'parking', }, { position: { lat: -33.919543720969806, lng: 151.23112279762267 }, type: 'parking', }, { position: { lat: -33.91608037421864, lng: 151.23288232673644 }, type: 'parking', }, { position: { lat: -33.91851096391805, lng: 151.2344058214569 }, type: 'parking', }, { position: { lat: -33.91818154739766, lng: 151.2346203981781 }, type: 'parking', }, { position: { lat: -33.91727341958453, lng: 151.23348314155578 }, type: 'library', }, ]; for (const feature of features) { const iconData = icons[feature.type]; const pin = new PinElement({ background: iconData.color, borderColor: iconData.color, glyphSrc: new URL( `data:image/svg+xml;charset=UTF-8,${encodeURIComponent(iconData.icon)}` ), scale: 1.5, }); const marker = new AdvancedMarkerElement({ position: feature.position, title: iconData.name, }); marker.append(pin); mapElement.append(marker); } } async function makeLegend() { const { PinElement } = await google.maps.importLibrary('marker'); const legend = document.getElementById('legend'); const title = document.createElement('div'); title.innerText = 'Legend'; title.classList.add('title'); legend.appendChild(title); for (const type of Object.values(icons)) { const wrapper = document.createElement('div'); wrapper.classList.add('legend-item'); const pin = new PinElement({ background: type.color, borderColor: type.color, glyphSrc: new URL( `data:image/svg+xml;charset=UTF-8,${encodeURIComponent(type.icon)}` ), scale: 1.0, }); // PinElement extends HTMLElement directly, so we can append it directly! const pinContainer = document.createElement('div'); pinContainer.classList.add('legend-icon'); pinContainer.appendChild(pin); const txt = document.createElement('div'); txt.classList.add('legend-text'); txt.innerText = type.name; wrapper.appendChild(pinContainer); wrapper.appendChild(txt); legend.appendChild(wrapper); } } void init();
CSS
html, body { height: 100%; margin: 0; padding: 0; } #legend { background-color: #fff; width: 15em; margin: 1em; border-radius: 8px; font-family: Roboto, Arial, sans-serif; overflow: hidden; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3); } .title { padding: 0.5em 1em; font-weight: bold; font-size: 1.5em; margin-bottom: 0.5em; background-color: rgb(66, 133, 244); color: white; box-sizing: border-box; } .legend-item { margin: 0.5em 1em; display: flex; flex-direction: row; align-items: center; padding-bottom: 0.5em; } .legend-icon { margin-right: 1em; font-size: 24px; } .legend-text { font-size: 1.5em; }
HTML
<html>
<head>
<title>Custom Legend</title>
<link rel="stylesheet" type="text/css" href="./style.css" />
<script type="module" src="./index.js"></script>
<script>
// prettier-ignore
(g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})({
key: "GOOGLE_MAPS_API_KEY"
});
</script>
</head>
<body>
<gmp-map center="-33.91722,151.23064" zoom="16.5" map-id="DEMO_MAP_ID">
<div id="legend" slot="control-inline-start-block-start"></div>
</gmp-map>
</body>
</html>Clone Sample
Git and Node.js are required to run this sample locally. Follow these instructions to install Node.js and NPM. The following commands clone, install dependencies and start the sample application.
git clone https://github.com/googlemaps-samples/js-api-samples.gitcd samples/legendnpm inpm start