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
onMountto avoid SSR errors. - Clean up with
map.remove()when the component unmounts. - Set
Icon.DefaultURLs so markers resolve in Vite builds. - Keep tiles and markers driven by props to avoid re-initializing the map.
- Prefer
invalidateor 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
onMountgates all Leaflet access.- Tile layer attribution is set.
- Cleanup removes listeners and DOM.