Skip to content

Leaflet.js Custom Controls — Building UI for Map Interactions

DodaTech Updated 2026-06-28 3 min read

In this tutorial, you will learn about Leaflet.js Custom Controls. We cover key concepts, practical examples, and best practices to help you master this topic.

Leaflet.js custom controls allow you to build and position custom UI elements on the map using L.Control with custom HTML, events, and positioning.

What You'll Learn

By the end of this guide, you will create custom controls with HTML content, position controls in any corner, handle control button click events, build a custom legend control, and create a toolbar with multiple buttons.

Basic Custom Control

var InfoControl = L.Control.extend({
    options: {
        position: 'topright'
    },
    onAdd: function(map) {
        var container = L.DomUtil.create('div', 'info-control leaflet-bar');
        container.innerHTML = '<button id="info-btn">Info</button>';
        container.style.backgroundColor = 'white';
        container.style.padding = '8px 12px';
        container.style.borderRadius = '4px';
        container.style.cursor = 'pointer';
        return container;
    }
});

map.addControl(new InfoControl());

Control with Events

var DrawControl = L.Control.extend({
    options: { position: 'topleft' },
    onAdd: function(map) {
        var container = L.DomUtil.create('div', 'draw-control leaflet-bar');
        container.innerHTML = '<button id="draw-btn" class="leaflet-control-button">Draw</button>';

        L.DomEvent.on(container, 'click', function() {
            map.fire('custom:draw');
        });

        L.DomEvent.disableClickPropagation(container);
        return container;
    }
});

Legend Control

var LegendControl = L.Control.extend({
    options: { position: 'bottomright' },
    onAdd: function() {
        var div = L.DomUtil.create('div', 'legend-control');
        div.innerHTML = '<h4>Legend</h4>' +
            '<div><span style="background:#4ecdc4;width:12px;height:12px;display:inline-block;margin-right:4px;"></span>Parks</div>' +
            '<div><span style="background:#ff6b35;width:12px;height:12px;display:inline-block;margin-right:4px;"></span>Buildings</div>';
        return div;
    }
});

Common Mistakes

1. Not Using L.DomEvent.disableClickPropagation

Clicks on controls propagate to the map. Always disable click propagation to prevent unwanted map interactions.

2. Missing CSS Styling

Custom controls need explicit CSS for background, border, and padding. Otherwise they inherit map styles.

3. Positioning Conflicts

Two controls at the same position overlap. Use different positions or stack them manually.

4. Not Returning Container from onAdd

onAdd must return a DOM element. Returning undefined causes errors.

5. Forgetting L.DomUtil.create

Use L.DomUtil.create for cross-browser element creation. Avoid innerHTML for dynamic content with user input.

Practice Questions

Q1: What method must every control implement? A: onAdd, which returns a DOM element to display on the map.

Q2: How do you set control position? A: Set the position option: 'topleft', 'topright', 'bottomleft', 'bottomright'.

Q3: How do you prevent click events from reaching the map? A: Call L.DomEvent.disableClickPropagation on the container element.

Q4: How do you update control content after creation? A: Access the container via the control instance and modify its innerHTML.

Q5: How do you remove a control? A: Call map.removeControl(controlInstance).

Challenge: Build a toolbar with 4 buttons (Zoom In, Zoom Out, Center, Info). Position it at the top center of the map. Each button should perform its action and show a brief tooltip on hover.

FAQ

Can I use React components as Leaflet controls?

Yes. Create a L.Control subclass that renders a React root in onAdd.

How do I make controls responsive?

Use CSS media queries on the control container class.

Can controls have keyboard focus?

Yes. Add tabindex to interactive elements inside the control.

How do I add animations to controls?

Use CSS transitions on the control container. Leaflet does not animate control appearance.

Can I create floating panels as controls?

Yes. Custom controls can be any HTML element. Use absolute positioning inside the control.

Try It Yourself

Build a map with a custom info control.

<!DOCTYPE html>
<html>
<head>
    <title>Custom Controls Demo</title>
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9/dist/leaflet.css" />
    <script src="https://unpkg.com/leaflet@1.9/dist/leaflet.js"></script>
    <style>
        .info-control { background: white; padding: 8px 14px; border-radius: 6px; box-shadow: 0 2px 6px rgba(0,0,0,0.2); font-size: 13px; cursor: default; }
        .info-control button { margin-left: 4px; cursor: pointer; border: 1px solid #ddd; background: white; border-radius: 4px; padding: 4px 10px; }
        .info-control button:hover { background: #f0f0f0; }
        .legend-box { display: inline-block; width: 12px; height: 12px; margin-right: 4px; border-radius: 2px; }
    </style>
</head>
<body style="font-family:sans-serif;padding:20px;">
<h2>Custom Controls</h2>
<div id="map" style="height:400px;border-radius:8px;"></div>
<script>
var map = L.map('map').setView([40.7128, -74.0060], 12);

L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
    attribution: '&copy; OSM'
}).addTo(map);

var LegendControl = L.Control.extend({
    options: { position: 'bottomright' },
    onAdd: function() {
        var div = L.DomUtil.create('div', 'info-control');
        div.innerHTML = '<b>Legend</b><br><span class="legend-box" style="background:#4ecdc4;"></span>Water<br><span class="legend-box" style="background:#2a9d8f;"></span>Parks<br><span class="legend-box" style="background:#ff6b35;"></span>Urban';
        return div;
    }
});

var CenterControl = L.Control.extend({
    options: { position: 'topleft' },
    onAdd: function(map) {
        var container = L.DomUtil.create('div', 'info-control leaflet-bar');
        container.innerHTML = '<button id="center-btn">Center NYC</button>';
        L.DomEvent.on(container, 'click', function() {
            map.setView([40.7128, -74.0060], 12);
        });
        L.DomEvent.disableClickPropagation(container);
        return container;
    }
});

map.addControl(new LegendControl());
map.addControl(new CenterControl());
</script>
</body>
</html>

What's Next

Build custom popups.

Popups — Custom popup content. Measurement — Measurement tools.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro