Get a Maps Demo Key: Try out select Maps JavaScript API and Places UI Kit features at no cost with a Maps Demo Key—no billing information required.

Add a custom legend

  • This tutorial demonstrates how to create and add a legend to a Google Map using custom controls.

  • The legend provides information about custom markers, such as parking, libraries, and info points, displayed on the map.

  • It leverages HTML for structure, CSS for styling, and JavaScript/TypeScript for dynamic content and map interaction.

  • You can customize the legend's appearance (font, background, border) and position (e.g., right-bottom) as needed.

  • The legend content is dynamically generated based on the defined marker styles, ensuring it reflects the map's markers.

Overview

Learn how to create a legend on a Google map through this tutorial. Legends generally describe the symbols and markers on a map. You can create them using the positioning feature of custom controls.

The map below contains a legend that provides information about three different custom markers on the map.

[フレーム]

The sections below explain the main steps to create a legend.

See the Complete code example section for the full code.

Create the legend container

Create the legend container by adding a div to the gmp-map element. Use control slotting to set the position of the legend. Read more about control position options.

<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>

Apply styles to set the visual appearance of the legend. This example styles the legend container, title, and item.

#legend{
background-color:#fff;
width:15em;
margin:1em;
border-radius:8px;
font-family:Roboto,Arial,sans-serif;
overflow:hidden;
box-shadow:02px6pxrgba(0,0,0,0.3);
}
.legend-item{
margin:0.5em1em;
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;
}

Add the legend content

The makeLegend() function programmatically builds a visual map legend and inserts it into the page's DOM. Here is how it works:

  1. Imports the Library: It dynamically imports the PinElement class from the Maps SDK (await google.maps.importLibrary('marker')).
const{PinElement}=awaitgoogle.maps.importLibrary('marker');
  1. Prepares the Container: It grabs the existing empty <div id="legend"> from the HTML and prepends a "Legend" title element to it.
constlegend=document.getElementById('legend')!;
consttitle=document.createElement('div');
title.innerText='Legend';
title.classList.add('title');
legend.appendChild(title);
  1. Iterates Over the Configurations: It loops through the icons configuration object to generate a row for each item (Parking, Library, Info). The helper function looks like this:

asyncfunctionmakeLegend(){
const{PinElement}=awaitgoogle.maps.importLibrary('marker');
constlegend=document.getElementById('legend');
consttitle=document.createElement('div');
title.innerText='Legend';
title.classList.add('title');
legend.appendChild(title);
for(consttypeofObject.values(icons)){
constwrapper=document.createElement('div');
wrapper.classList.add('legend-item');
constpin=newPinElement({
background:type.color,
borderColor:type.color,
glyphSrc:newURL(
`data:image/svg+xml;charset=UTF-8,${encodeURIComponent(type.icon)}`
),
scale:1.0,
});
// PinElement extends HTMLElement directly, so we can append it directly!
constpinContainer=document.createElement('div');
pinContainer.classList.add('legend-icon');
pinContainer.appendChild(pin);
consttxt=document.createElement('div');
txt.classList.add('legend-text');
txt.innerText=type.name;
wrapper.appendChild(pinContainer);
wrapper.appendChild(txt);
legend.appendChild(wrapper);
}
}

See Style point data features for another example of a programmatically generated legend.

Complete code example

The section below displays the entire code you need to create the map and legend in this tutorial.

TypeScript

constparkingSvg=`<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>`;
constlibrarySvg=`<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>`;
constinfoSvg=`<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>`;
consticons: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
},
};
asyncfunctioninit():Promise<void>{
const[{AdvancedMarkerElement,PinElement},{event}]=
awaitPromise.all([
google.maps.importLibrary('marker'),
google.maps.importLibrary('core'),
google.maps.importLibrary('maps'),
]);
constmapElement=document.querySelector('gmp-map')!;
constinnerMap=mapElement.innerMap;
// Wait for the map to load before building the legend.
event.addListenerOnce(innerMap,'idle',()=>{
voidmakeLegend();
});
constfeatures=[
{
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(constfeatureoffeatures){
consticonData=icons[feature.type];
constpin=newPinElement({
background:iconData.color,
borderColor:iconData.color,
glyphSrc:newURL(
`data:image/svg+xml;charset=UTF-8,${encodeURIComponent(iconData.icon)}`
),
scale:1.5,
});
constmarker=newAdvancedMarkerElement({
position:feature.position,
title:iconData.name,
});
marker.append(pin);
mapElement.append(marker);
}
}
asyncfunctionmakeLegend(){
const{PinElement}=awaitgoogle.maps.importLibrary('marker');
constlegend=document.getElementById('legend')!;
consttitle=document.createElement('div');
title.innerText='Legend';
title.classList.add('title');
legend.appendChild(title);
for(consttypeofObject.values(icons)){
constwrapper=document.createElement('div');
wrapper.classList.add('legend-item');
constpin=newPinElement({
background:type.color,
borderColor:type.color,
glyphSrc:newURL(
`data:image/svg+xml;charset=UTF-8,${encodeURIComponent(type.icon)}`
),
scale:1.0,
});
// PinElement extends HTMLElement directly, so we can append it directly!
constpinContainer=document.createElement('div');
pinContainer.classList.add('legend-icon');
pinContainer.appendChild(pin);
consttxt=document.createElement('div');
txt.classList.add('legend-text');
txt.innerText=type.name;
wrapper.appendChild(pinContainer);
wrapper.appendChild(txt);
legend.appendChild(wrapper);
}
}
voidinit();

JavaScript

constparkingSvg=`<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>`;
constlibrarySvg=`<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>`;
constinfoSvg=`<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>`;
consticons={
parking:{
name:'Parking',
icon:parkingSvg,
color:'#1E88E5',// Blue
},
library:{
name:'Library',
icon:librarySvg,
color:'#43A047',// Green
},
info:{
name:'Info',
icon:infoSvg,
color:'#E53935',// Red
},
};
asyncfunctioninit(){
const[{AdvancedMarkerElement,PinElement},{event}]=
awaitPromise.all([
google.maps.importLibrary('marker'),
google.maps.importLibrary('core'),
google.maps.importLibrary('maps'),
]);
constmapElement=document.querySelector('gmp-map');
constinnerMap=mapElement.innerMap;
// Wait for the map to load before building the legend.
event.addListenerOnce(innerMap,'idle',()=>{
voidmakeLegend();
});
constfeatures=[
{
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(constfeatureoffeatures){
consticonData=icons[feature.type];
constpin=newPinElement({
background:iconData.color,
borderColor:iconData.color,
glyphSrc:newURL(
`data:image/svg+xml;charset=UTF-8,${encodeURIComponent(iconData.icon)}`
),
scale:1.5,
});
constmarker=newAdvancedMarkerElement({
position:feature.position,
title:iconData.name,
});
marker.append(pin);
mapElement.append(marker);
}
}
asyncfunctionmakeLegend(){
const{PinElement}=awaitgoogle.maps.importLibrary('marker');
constlegend=document.getElementById('legend');
consttitle=document.createElement('div');
title.innerText='Legend';
title.classList.add('title');
legend.appendChild(title);
for(consttypeofObject.values(icons)){
constwrapper=document.createElement('div');
wrapper.classList.add('legend-item');
constpin=newPinElement({
background:type.color,
borderColor:type.color,
glyphSrc:newURL(
`data:image/svg+xml;charset=UTF-8,${encodeURIComponent(type.icon)}`
),
scale:1.0,
});
// PinElement extends HTMLElement directly, so we can append it directly!
constpinContainer=document.createElement('div');
pinContainer.classList.add('legend-icon');
pinContainer.appendChild(pin);
consttxt=document.createElement('div');
txt.classList.add('legend-text');
txt.innerText=type.name;
wrapper.appendChild(pinContainer);
wrapper.appendChild(txt);
legend.appendChild(wrapper);
}
}
voidinit();

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:02px6pxrgba(0,0,0,0.3);
}
.title{
padding:0.5em1em;
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.5em1em;
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>

Except as otherwise noted, the content of this page is licensed under the Creative Commons Attribution 4.0 License, and code samples are licensed under the Apache 2.0 License. For details, see the Google Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.

Last updated 2026年09月01日 UTC.