Skip to content
Guide

Leaflet maps in SvelteKit

Leaflet expects a browser environment. Keep the map client-only, clean up on destroy, and ship icons through the Vite asset pipeline.

Best practices

  • Initialize Leaflet inside onMount to avoid SSR errors.
  • Clean up with map.remove() when the component unmounts.
  • Set Icon.Default URLs so markers resolve in Vite builds.
  • Keep tiles and markers driven by props to avoid re-initializing the map.
  • Prefer invalidate or explicit refresh to update data instead of full reloads.

SvelteKit-friendly setup

<script lang="ts">
	import { onMount } from "svelte";
	import "leaflet/dist/leaflet.css";
	import markerIcon2x from "leaflet/dist/images/marker-icon-2x.png";
	import markerIcon from "leaflet/dist/images/marker-icon.png";
	import markerShadow from "leaflet/dist/images/marker-shadow.png";

	let mapEl: HTMLDivElement | null = null;
	let map: import("leaflet").Map | null = null;

	onMount(async () => {
		const L = await import("leaflet");

		const defaultIcon = L.icon({
			iconRetinaUrl: markerIcon2x,
			iconUrl: markerIcon,
			shadowUrl: markerShadow,
			iconSize: [25, 41],
			iconAnchor: [12, 41],
			popupAnchor: [1, -34],
			shadowSize: [41, 41]
		});

		map = L.map(mapEl!).setView([37.7749, -122.4194], 12);
		L.marker([37.7749, -122.4194], { icon: defaultIcon }).addTo(map);
		return () => map?.remove();
	});
</script>

Checklist

  • onMount gates all Leaflet access.
  • Tile layer attribution is set.
  • Cleanup removes listeners and DOM.
Built as a personal SvelteKit 5 lab with Supabase auth. Guides, patterns, and a playground you can actually ship.
Command Palette
Search for a command to run