Leaflet.js Basics — Setup, Map Creation & Tile Layers
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
<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:
- Bottom sheet — the base map tiles (roads, terrain, satellite imagery)
- Middle sheets — markers, circles, polygons (the interactive elements)
- 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 leafletThen 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: "© 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">. TheLobject 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 tofalseso 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: "© <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: "© OpenStreetMap contributors © CARTO",
maxZoom: 19
});OpenTopoMap (Topographic)
L.tileLayer("https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png", {
attribution: "© OpenTopoMap contributors",
maxZoom: 17
});When to Use Each Tile Provider
| Provider | Best For | Requires API Key |
|---|---|---|
| OpenStreetMap | General-purpose maps | No |
| CartoDB Dark | Dashboard backgrounds | No |
| OpenTopoMap | Hiking/outdoor apps | No |
| Mapbox | Custom branded maps | Yes |
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
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: "© OpenStreetMap contributors"
}),
dark: L.tileLayer("https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png", {
attribution: "© CARTO"
}),
topo: L.tileLayer("https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png", {
attribution: "© 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
| Lesson | Description |
|---|---|
| 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