Skip to content
Leaflet.js Basics — Setup, Map Creation & Tile Layers

Leaflet.js Basics — Setup, Map Creation & Tile Layers

DodaTech Updated Jun 6, 2026 9 min read

Leaflet.js is a lightweight, open-source JavaScript library for creating interactive maps in web pages, handling tile layers, markers, and user interactions with just a few lines of code.

What You’ll Learn

By the end of this tutorial, you will be able to create a full-screen map from scratch, understand how tile layers work, configure different tile providers, handle geographic coordinates, and control the map view programmatically.

Why Leaflet.js Basics Matters

Interactive maps power everything from store locators to delivery tracking. Doda Browser uses Leaflet.js internally for location-based bookmark features, and Durga Antivirus Pro leverages geographic threat visualization to show attack origins in real time. Understanding map fundamentals lets you build location-aware applications that your users expect.

    flowchart LR
  A["Leaflet.js Basics 🗺️"] --> B["Markers & Icons"]
  B --> C["Shapes & GeoJSON"]
  C --> D["Layers & Controls"]
  D --> E["Events"]
  E --> F["Advanced & Plugins"]
  
  A:::current
  B:::future
  C:::future
  D:::future
  E:::future
  F:::future
  
  classDef current fill:#e91e63,stroke:#333,color:#fff
  classDef future fill:#f5f5f5,stroke:#999,color:#666
  
You need basic knowledge of HTML, CSS, and JavaScript. If you can create an HTML file and understand what <script> and <style> tags do, you’re ready.

What Is Leaflet.js?

Think of a map on a website like a stack of transparent tracing paper sheets:

  1. Bottom sheet — the base map tiles (roads, terrain, satellite imagery)
  2. Middle sheets — markers, circles, polygons (the interactive elements)
  3. Top sheets — popups, tooltips, controls (what the user interacts with)

Leaflet manages this stack. You tell it what to put on each layer, and it handles the rest — panning, zooming, touch gestures, and mobile support.

Why Leaflet and Not Google Maps?

Leaflet is free, open-source (BSD licensed), and lightweight (~42KB). Google Maps requires an API key, has rate limits, and can cost money at scale. For most projects, Leaflet provides everything you need without vendor lock-in.

Installation

Method 1: CDN (Quickest Way)

Add these two lines inside your HTML <head>:

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

The <link> loads Leaflet’s CSS (controls, popups, markers). The <script> loads the JavaScript library. Both come from the unpkg CDN, so no local files are needed.

Method 2: npm (For Build Tools)

npm install leaflet

Then in your JavaScript file:

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

Your First Map

Let’s create a map centered on London. Here’s the complete HTML:

<!DOCTYPE html>
<html>
<head>
    <title>My First Map</title>
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
    <style>
        #map { height: 400px; }
    </style>
</head>
<body>
    <h2>My First Leaflet Map</h2>
    <div id="map"></div>

    <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
    <script>
        var map = L.map("map").setView([51.505, -0.09], 13);

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

Let’s explain each part:

  • <div id="map"></div> — This is the container where the map renders. It must have a CSS height or it will be invisible (zero-height).
  • #map { height: 400px; } — Without explicit height, the map won’t show. This is the #1 mistake beginners make.
  • L.map("map") — Creates a new map inside the <div id="map">. The L object is Leaflet’s global namespace.
  • .setView([51.505, -0.09], 13) — Centers the map at latitude 51.505, longitude -0.09 (London), zoom level 13. This chaining pattern is common in Leaflet.
  • L.tileLayer(...) — Creates the base map using tile images from OpenStreetMap.
  • {s} in the URL — Leaflet replaces {s} with subdomains (a, b, c) automatically for parallel tile loading.
  • {z}/{x}/{y} — Zoom level, x coordinate, y coordinate — the standard tile numbering scheme.
  • .addTo(map) — Attaches the tile layer to our map. Without this, nothing shows.

Map Configuration Options

You can pass all settings when creating the map:

var map = L.map("mapId", {
    center: [51.505, -0.09],
    zoom: 13,
    zoomControl: true,       // Show +/- buttons
    scrollWheelZoom: true,   // Mouse wheel zoom
    doubleClickZoom: true,   // Double-click to zoom
    touchZoom: true,         // Pinch-to-zoom on mobile
    dragging: true,          // Pan by dragging
    keyboard: true,          // Arrow key navigation
    zoomSnap: 1,             // Snap to integer zoom levels
    minZoom: 1,              // Minimum zoom level
    maxZoom: 19,             // Maximum zoom level
    inertia: true            // Smooth panning deceleration
});

Why These Options Matter

  • scrollWheelZoom — Accidental scroll-wheel zoom frustrates users. Disable it on maps embedded in scrolling pages.
  • zoomControl — If you build a custom zoom UI, set this to false so you don’t get duplicate buttons.
  • inertia — Creates a smooth “coasting” effect after flinging the map on touch devices. Without it, panning feels jerky.

Tile Layers

Tile layers are the base map imagery. Think of them like puzzle pieces — each tile is a 256×256 pixel image. When you pan or zoom, Leaflet requests new tiles from the server.

OpenStreetMap (Free with Attribution)

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

CartoDB Dark Mode

L.tileLayer("https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png", {
    attribution: "&copy; OpenStreetMap contributors &copy; CARTO",
    maxZoom: 19
});

OpenTopoMap (Topographic)

L.tileLayer("https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png", {
    attribution: "&copy; OpenTopoMap contributors",
    maxZoom: 17
});

When to Use Each Tile Provider

ProviderBest ForRequires API Key
OpenStreetMapGeneral-purpose mapsNo
CartoDB DarkDashboard backgroundsNo
OpenTopoMapHiking/outdoor appsNo
MapboxCustom branded mapsYes

Coordinates & Viewport

Leaflet uses [latitude, longitude] order. This is important because GeoJSON (covered later) uses [longitude, latitude] — the opposite.

// Correct Leaflet order
var point = [40.7128, -74.0060];  // New York [lat, lng]

// Wrong!
var wrong = [-74.0060, 40.7128];  // This places you somewhere unexpected

Controlling the View

map.setView([48.8566, 2.3522], 10);       // Move center + set zoom
map.setZoom(14);                            // Change zoom only
map.panTo([48.8566, 2.3522]);              // Smooth pan
map.flyTo([48.8566, 2.3522], 12);          // Animated flight (like Google Earth)

flyTo() creates a smooth zoom-out, pan, zoom-in animation. Use it for dramatic transitions. Use panTo() for quick, functional moves.

Getting Current State

map.getCenter();        // {lat: ..., lng: ...}
map.getZoom();          // 13
map.getBounds();        // Southwest and northeast corners

Fitting the Map to Show Multiple Points

var bounds = L.latLngBounds(
    [40.7128, -74.0060],    // Southwest (NYC)
    [34.0522, -118.2437]    // Northeast (LA)
);

map.fitBounds(bounds, {
    padding: [50, 50],       // 50px padding on all sides
    maxZoom: 15
});

This is useful when you add multiple markers and want to show them all at once.

Common Mistakes

1. Map container has no CSS height

The map <div> needs a height. Without it, the div is 0px tall and invisible.

/* Always include this */
#map { height: 400px; }
/* Or full viewport */
#map { height: 100vh; }

2. Using [longitude, latitude] instead of [latitude, longitude]

Leaflet expects [lat, lng]. Getting this wrong places your map in the wrong hemisphere. Double-check every time.

3. Creating a tile layer without adding it to the map

L.tileLayer(url, options) returns a layer object. It does nothing until you call .addTo(map).

4. Zoom level exceeds the tile provider’s maximum

If your maxZoom is 20 but OpenStreetMap only serves up to zoom 19, tiles at zoom 20 will be blank. Always match maxZoom to the provider’s limit.

5. Not including attribution

OpenStreetMap tiles require attribution. Omitting it violates their terms of service and can get your app blocked.

Practice Questions

Q1: What does L.map("map") expect as its first argument? A: The id attribute of the <div> element where the map should render.

Q2: Why does L.tileLayer(...) need .addTo(map) at the end? A: Because creating a tile layer only configures it — .addTo(map) actually attaches it to the map instance.

Q3: What happens if you omit height from the map container’s CSS? A: The map div collapses to 0px height and becomes invisible. The browser renders an empty space.

Q4: What coordinate order does Leaflet use? A: [latitude, longitude]. GeoJSON uses the opposite: [longitude, latitude].

Q5: What is the difference between panTo() and flyTo()? A: panTo() moves the map instantly. flyTo() animates with a zoom-out, pan, zoom-in effect for dramatic transitions.

Challenge: Create a map that centers on your current city at zoom 14, using CartoDB Dark tiles. Add a marker at your local landmark. Then add code to log the map center to the console every time the user pans.

FAQ

Is Leaflet.js free?
Yes, Leaflet is BSD-licensed open source. OpenStreetMap tiles are also free with attribution. Premium providers like Mapbox charge for high-volume tile usage.
What is the difference between Leaflet and Google Maps API?
Leaflet is free, lightweight (~42KB), and open-source. Google Maps requires a paid API key for production use and is much heavier. Both provide similar core functionality.
Can Leaflet work offline?
Yes, if you cache tile images. Leaflet itself has no offline dependencies. You can serve tiles from a local tile server or use a service worker.
What projection does Leaflet use?
Leaflet defaults to EPSG:3857 (Web Mercator) for tile display, with convenience methods in EPSG:4326 (lat/lng).
How do I use a custom tile server?
Pass the custom URL with {z}/{x}/{y} placeholders to L.tileLayer(). Point it at your own tile server or use a third-party provider.

Try It Yourself

Here’s a complete runnable HTML sandbox that creates a map with four switchable tile providers:

<!DOCTYPE html>
<html>
<head>
    <title>Tile Provider Switcher</title>
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
    <style>
        body { font-family: sans-serif; padding: 20px; }
        #map { height: 400px; border-radius: 8px; }
        .controls { margin: 10px 0; }
        .controls button { padding: 8px 16px; margin: 0 5px 5px 0;
            border: 1px solid #ddd; border-radius: 4px; cursor: pointer;
            background: #f8f9fa; }
        .controls button.active { background: #4CAF50; color: white; border-color: #4CAF50; }
    </style>
</head>
<body>
    <h2>Map Provider Switcher</h2>
    <div class="controls">
        <button class="active" onclick="setTile('osm')">OpenStreetMap</button>
        <button onclick="setTile('dark')">Dark Mode</button>
        <button onclick="setTile('topo')">Topographic</button>
    </div>
    <div id="map"></div>

    <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
    <script>
        var map = L.map("map").setView([20, 0], 2);

        var tiles = {
            osm: L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
                attribution: "&copy; OpenStreetMap contributors"
            }),
            dark: L.tileLayer("https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png", {
                attribution: "&copy; CARTO"
            }),
            topo: L.tileLayer("https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png", {
                attribution: "&copy; OpenTopoMap"
            })
        };

        var currentLayer = tiles.osm.addTo(map);

        function setTile(name) {
            if (currentLayer) map.removeLayer(currentLayer);
            currentLayer = tiles[name].addTo(map);
            document.querySelectorAll(".controls button").forEach(function(btn) {
                btn.classList.remove("active");
            });
            event.target.classList.add("active");
        }
    </script>
</body>
</html>

What’s Next

LessonDescription
Markers & Icons →Custom markers, popups, tooltips, and clustering
Shapes & GeoJSON →Circles, polylines, polygons, and GeoJSON data
Layers & Controls →Layer management, image overlays, custom controls

Related Topics: JavaScript fundamentals | HTML structure | CSS positioning

What’s Next

Congratulations on completing this Leafletjs Basics tutorial! Here’s where to go from here:

  • Practice daily — Consistency is more important than long study sessions
  • Build a project — Apply what you learned by building something real
  • Explore related topics — Check out other tutorials in the same category
  • Join the community — Discuss with other learners and share your progress

Remember: every expert was once a beginner. Keep coding!

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro