← Back to Blog

Getting Started with Leaflet.js: Maps for Beginners

Learn Leaflet.js from scratch — create maps, add markers, load GeoJSON layers, and handle events. Follow this beginner guide and try it in GeoDataTools.

Leaflet.js is a lightweight, open-source JavaScript library for building interactive maps. At roughly 42 KB gzipped, it delivers tile layers, markers, popups, vector overlays, and a clean event system without the bulk of full-featured GIS frameworks. This guide walks through everything a beginner needs: installing Leaflet.js via CDN or npm, rendering a map, placing markers with popups, switching tile layers, loading GeoJSON data, wiring up events, and making your map work on mobile devices.

Why Choose Leaflet.js?

Leaflet prioritizes simplicity and performance. It handles the most common mapping tasks out of the box and delegates specialized functionality — clustering, heatmaps, drawing tools — to a plugin ecosystem with hundreds of extensions. Sites like GitHub, Flickr, and the Washington Post use Leaflet in production. If you need a deeper feature comparison with other frameworks, see our OpenLayers vs Leaflet comparison.

Installation

CDN Setup

The quickest way to start is loading Leaflet from a CDN. Add the stylesheet and script to your HTML <head>:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>My First Leaflet Map</title>
  <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
  <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
  <style>
    #map { height: 500px; }
  </style>
</head>
<body>
  <div id="map"></div>
</body>
</html>

npm Installation

For projects using Webpack, Vite, or Angular CLI, install Leaflet through npm:

npm install leaflet
npm install -D @types/leaflet  # TypeScript type definitions

Then import Leaflet in your module:

import L from 'leaflet';
import 'leaflet/dist/leaflet.css';

Creating Your First Map

A Leaflet map requires a container element with a defined height and a few lines of JavaScript. Call L.map() with the container ID, set the initial view coordinates and zoom level, and add a tile layer:

<div id="map" style="height: 500px;"></div>

<script>
  const map = L.map('map').setView([51.505, -0.09], 13);

  L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
    maxZoom: 19,
    attribution: '&copy; OpenStreetMap contributors'
  }).addTo(map);
</script>

setView() accepts a latitude/longitude array and a zoom level (0–18, where 0 shows the entire world). The tile layer fetches map images from OpenStreetMap using the standard {z}/{x}/{y} URL template. Without a tile layer, the map canvas is blank — tiles provide the visual base for your data.

Adding Markers and Popups

Markers pin a location on the map. Popups display HTML content when a user clicks a marker or other layer element.

const marker = L.marker([51.5, -0.09]).addTo(map);
marker.bindPopup('<strong>Hello!</strong><br>I am a popup.');
marker.openPopup();

Popups support full HTML, so you can embed images, tables, or links. You can also bind popups to circles, polygons, and GeoJSON layers using the same bindPopup() method.

To replace the default blue marker with a custom icon:

const customIcon = L.icon({
  iconUrl: 'marker-red.png',
  iconSize: [25, 41],
  iconAnchor: [12, 41],
  popupAnchor: [1, -34]
});
L.marker([51.5, -0.09], { icon: customIcon }).addTo(map);

Tile Layers

Tile layers form the visual foundation of every web map. Leaflet works with any tile provider that follows the {z}/{x}/{y} URL pattern. Here are the most common options:

ProviderStyleAPI KeyBest For
OpenStreetMapGeneral-purposeNoFree projects, prototyping
MapboxCustom designsYesBranded, styled maps
CartoDB / CARTOMinimal light/darkNoData visualization overlays
Stadia MapsStamen-inspiredYesArtistic, watercolor styles

Use L.control.layers() to let users toggle between multiple base layers — for example, satellite imagery, terrain, and street views — within a single application.

Loading GeoJSON Data

GeoJSON is the standard format for representing geographic features on the web, defined by RFC 7946. Leaflet has built-in support through L.geoJSON(), which reads a GeoJSON object and creates the corresponding vector layers — points, lines, or polygons:

const geojsonData = {
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "geometry": { "type": "Point", "coordinates": [-0.09, 51.505] },
      "properties": { "name": "London", "population": 8982000 }
    }
  ]
};

L.geoJSON(geojsonData, {
  onEachFeature: (feature, layer) => {
    layer.bindPopup(feature.properties.name);
  },
  style: (feature) => ({
    color: '#3388ff',
    weight: 2,
    fillOpacity: 0.3
  })
}).addTo(map);

The onEachFeature callback runs for every feature, letting you bind popups, attach listeners, or apply conditional styles. The style option controls fill color, line weight, and opacity for polygon and line features. Use pointToLayer to replace default markers with circle markers or custom icons on point features.

To prepare, inspect, or filter GeoJSON data before loading it into Leaflet, use GeoDataTools. You can also convert KML to GeoJSON or convert GeoJSON to KML depending on your source format.

Event Handling

Leaflet uses a straightforward event model. Attach listeners to the map or any layer with .on():

map.on('click', (e) => {
  const { lat, lng } = e.latlng;
  L.popup()
    .setLatLng(e.latlng)
    .setContent('Clicked at: ' + lat.toFixed(5) + ', ' + lng.toFixed(5))
    .openOn(map);
});

// Highlight GeoJSON features on hover
geojsonLayer.on('mouseover', (e) => {
  e.target.setStyle({ weight: 3, color: '#ff6600' });
});
geojsonLayer.on('mouseout', (e) => {
  geojsonLayer.resetStyle(e.target);
});

Common map events include click, zoomend, moveend, resize, and locationfound. Layer events like mouseover, mouseout, and click enable interactive features such as hover highlights, feature selection, and dynamic info panels. The event object provides geographic coordinates, the originating layer, and the raw browser event.

Mobile Responsiveness

Leaflet supports touch interactions — pinch-to-zoom, drag-to-pan, and tap-to-click — without extra configuration. These gestures work on both iOS and Android devices. To optimize for mobile screens, include the viewport meta tag and size the map container responsively:

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<style>
  html, body { margin: 0; padding: 0; height: 100%; }
  #map { width: 100%; height: 100vh; }
</style>

Leaflet's built-in controls are already touch-friendly with appropriately sized tap targets. Consider disabling the zoom control on small screens and relying on pinch gestures instead. For apps that need geolocation, call map.locate({ setView: true, maxZoom: 16 }) to center the map on the user's position using the browser Geolocation API.

Next Steps

With the fundamentals in place, explore the Leaflet plugin ecosystem for advanced capabilities:

  • Leaflet.markercluster — Groups dense point data into expandable clusters
  • Leaflet.draw — Lets users draw and edit shapes directly on the map
  • Leaflet.heat — Renders heatmap visualizations from point data
  • Leaflet.Routing — Adds turn-by-turn routing and directions

If you need to prepare GeoJSON data for your Leaflet project — filtering features, validating geometry, or converting from other formats — GeoDataTools provides a browser-based workflow that produces clean, standards-compliant GeoJSON ready for L.geoJSON().

FAQ

Is Leaflet.js free to use?

Yes. Leaflet is released under the BSD 2-Clause license, making it free for personal and commercial projects. The library is open source, and community tile providers like OpenStreetMap are also free to use. Some premium tile services (Mapbox, Google Maps) require API keys and may charge based on usage volume.

Can Leaflet handle large GeoJSON datasets?

Leaflet renders GeoJSON as SVG or Canvas elements in the browser, so performance depends on the number of features and their geometric complexity. A few thousand simple features render smoothly. For tens of thousands of points, use the Leaflet.markercluster plugin to group nearby markers. For complex polygon datasets, consider simplifying geometries server-side or using vector tiles instead of raw GeoJSON. You can inspect and filter large datasets in GeoDataTools before loading them into Leaflet.

How do I add GeoJSON data to a Leaflet map?

Pass your GeoJSON object to L.geoJSON(data).addTo(map). Leaflet automatically creates the appropriate layers for each feature type. Use the onEachFeature option to bind popups or event listeners, and the style option to control visual appearance. If your data is in KML format, convert it to GeoJSON first, since Leaflet does not parse KML natively.

Ready to work with your geospatial data?

Visualize, filter, and convert GeoJSON and KML files directly in your browser.

Try GeoDataTools