Web Mapping Developer Guide

A comprehensive guide to building interactive web maps with GeoJSON Leaflet and modern tools.

The Web Mapping Stack

Every web map is built from the same core pieces: a basemap (tiles that provide the visual backdrop), data layers (your GeoJSON features drawn on top), and interaction handlers (zoom, pan, click, hover). The library you choose determines how these pieces fit together.

GeoJSON is the lingua franca of web mapping. Every major library can load and render it natively. This guide covers the most popular platforms and how each one works with GeoJSON.

Choosing a Library

LibraryRenderingLicenseBest For
LeafletSVG / CanvasBSD-2 (free)Simple maps, quick prototypes, small datasets
Mapbox GL JSWebGLProprietary (free tier)High-performance maps, large datasets, 3D
MapLibre GL JSWebGLBSD-3 (free)Same as Mapbox GL JS but fully open-source
OpenLayersCanvas / WebGLBSD-2 (free)Enterprise GIS, complex projections, OGC standards
ArcGIS Maps SDK for JSWebGLProprietary (free tier)Enterprise GIS, Esri ecosystem, 3D scenes
Google Maps JS APIWebGLProprietary (pay-per-use)Google ecosystem, Places/Geocoding integration
Deck.glWebGLMIT (free)Large-scale data visualization, GPU-accelerated layers
CesiumJSWebGLApache 2.0 (free)3D globes, terrain, time-dynamic data

Leaflet

Leaflet is the most popular open-source mapping library. It's lightweight (~42 KB gzipped), simple to learn, and has a massive plugin ecosystem. It renders GeoJSON using SVG or Canvas.

Basic GeoJSON Example

JavaScript (Leaflet)import L from "leaflet";

const map = L.map("map").setView([40.7, -74.0], 10);

L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
  attribution: "© OpenStreetMap contributors"
}).addTo(map);

// Add GeoJSON directly
L.geoJSON(geojsonData).addTo(map);

Styling and Interaction

JavaScript (Leaflet)L.geoJSON(geojsonData, {
  style: (feature) => ({
    color: feature.properties.type === "park" ? "#2d6a4f" : "#e63946",
    weight: 2,
    fillOpacity: 0.4
  }),
  onEachFeature: (feature, layer) => {
    layer.bindPopup(feature.properties.name);
  },
  pointToLayer: (feature, latlng) => {
    return L.circleMarker(latlng, { radius: 8 });
  }
}).addTo(map);

Loading from a URL

JavaScript (Leaflet)fetch("/data/parks.geojson")
  .then(res => res.json())
  .then(data => {
    const layer = L.geoJSON(data).addTo(map);
    map.fitBounds(layer.getBounds());
  });

Limitations: Leaflet uses SVG/Canvas rendering, which slows down with more than a few thousand features. For larger datasets, consider Mapbox GL JS, MapLibre, or clustering plugins like leaflet.markercluster.

Mapbox GL JS

Mapbox GL JS uses WebGL for GPU-accelerated rendering, enabling smooth handling of large datasets, 3D terrain, and custom vector styles. It requires a Mapbox access token.

Adding a GeoJSON Source and Layer

JavaScript (Mapbox GL JS)import mapboxgl from "mapbox-gl";

mapboxgl.accessToken = "YOUR_TOKEN";

const map = new mapboxgl.Map({
  container: "map",
  style: "mapbox://styles/mapbox/light-v11",
  center: [-74.0, 40.7],
  zoom: 10
});

map.on("load", () => {
  map.addSource("parks", {
    type: "geojson",
    data: "/data/parks.geojson"
  });

  map.addLayer({
    id: "parks-fill",
    type: "fill",
    source: "parks",
    paint: {
      "fill-color": "#2d6a4f",
      "fill-opacity": 0.4
    }
  });

  map.addLayer({
    id: "parks-outline",
    type: "line",
    source: "parks",
    paint: {
      "line-color": "#1b4332",
      "line-width": 2
    }
  });
});

Data-Driven Styling

JavaScriptmap.addLayer({
  id: "points",
  type: "circle",
  source: "my-data",
  paint: {
    "circle-radius": ["interpolate", ["linear"], ["get", "magnitude"], 1, 4, 6, 20],
    "circle-color": ["match", ["get", "type"],
      "park", "#2d6a4f",
      "school", "#457b9d",
      "#e63946"
    ]
  }
});

Click Interaction

JavaScript (Mapbox GL JS)map.on("click", "parks-fill", (e) => {
  const props = e.features[0].properties;
  new mapboxgl.Popup()
    .setLngLat(e.lngLat)
    .setHTML(`<h3>${props.name}</h3><p>${props.description}</p>`)
    .addTo(map);
});

MapLibre GL JS

MapLibre GL JS is the open-source fork of Mapbox GL JS (from v1). The API is nearly identical to Mapbox GL JS, but it's BSD-licensed and works with any vector tile source.

JavaScript (MapLibre GL JS)import maplibregl from "maplibre-gl";

const map = new maplibregl.Map({
  container: "map",
  style: "https://demotiles.maplibre.org/style.json",
  center: [-74.0, 40.7],
  zoom: 10
});

map.on("load", () => {
  map.addSource("my-data", {
    type: "geojson",
    data: geojsonData
  });

  map.addLayer({
    id: "my-layer",
    type: "fill",
    source: "my-data",
    paint: {
      "fill-color": "#088",
      "fill-opacity": 0.5
    }
  });
});

If you're using Mapbox GL JS code, migrating to MapLibre usually requires changing the import and providing your own tile source — the GeoJSON API is the same.

OpenLayers

OpenLayers is a full-featured open-source library with strong support for OGC standards (WMS, WFS, WMTS), custom projections, and enterprise GIS workflows. It has a steeper learning curve but more flexibility for complex use cases.

JavaScript (OpenLayers)import Map from "ol/Map";
import View from "ol/View";
import TileLayer from "ol/layer/Tile";
import VectorLayer from "ol/layer/Vector";
import VectorSource from "ol/source/Vector";
import OSM from "ol/source/OSM";
import GeoJSON from "ol/format/GeoJSON";
import { Style, Fill, Stroke } from "ol/style";

const vectorSource = new VectorSource({
  url: "/data/parks.geojson",
  format: new GeoJSON()
});

const map = new Map({
  target: "map",
  layers: [
    new TileLayer({ source: new OSM() }),
    new VectorLayer({
      source: vectorSource,
      style: new Style({
        fill: new Fill({ color: "rgba(45, 106, 79, 0.4)" }),
        stroke: new Stroke({ color: "#1b4332", width: 2 })
      })
    })
  ],
  view: new View({
    center: [0, 0],
    zoom: 2
  })
});

Note: OpenLayers uses EPSG:3857 (Web Mercator) for its view by default. The GeoJSON format reader automatically reprojects from EPSG:4326.

ArcGIS Maps SDK for JavaScript

Esri's ArcGIS Maps SDK for JavaScript is the mapping library for the ArcGIS platform. It supports 2D maps and 3D scenes, integrates with ArcGIS Online and Enterprise services, and includes advanced tools like spatial analysis, geocoding, and routing out of the box.

Loading GeoJSON

JavaScript (ArcGIS)import Map from "@arcgis/core/Map";
import MapView from "@arcgis/core/views/MapView";
import GeoJSONLayer from "@arcgis/core/layers/GeoJSONLayer";

const geojsonLayer = new GeoJSONLayer({
  url: "/data/parks.geojson",
  renderer: {
    type: "simple",
    symbol: {
      type: "simple-fill",
      color: [45, 106, 79, 0.4],
      outline: { color: [27, 67, 50], width: 2 }
    }
  },
  popupTemplate: {
    title: "{name}",
    content: "{description}"
  }
});

const map = new Map({
  basemap: "topo-vector",
  layers: [geojsonLayer]
});

const view = new MapView({
  container: "map",
  map: map,
  center: [-74.0, 40.7],
  zoom: 10
});

Data-Driven Rendering

JavaScript (ArcGIS)const geojsonLayer = new GeoJSONLayer({
  url: "/data/points.geojson",
  renderer: {
    type: "unique-value",
    field: "type",
    uniqueValueInfos: [
      {
        value: "park",
        symbol: { type: "simple-marker", color: "#2d6a4f", size: 10 }
      },
      {
        value: "school",
        symbol: { type: "simple-marker", color: "#457b9d", size: 10 }
      }
    ]
  }
});

Loading Inline GeoJSON

JavaScript// Create a Blob URL from inline GeoJSON data
const blob = new Blob([JSON.stringify(geojsonData)], {
  type: "application/json"
});
const url = URL.createObjectURL(blob);

const layer = new GeoJSONLayer({ url });

Note: GeoJSONLayer requires a URL — for inline data, create a Blob URL as shown above. The ArcGIS Maps SDK also supports FeatureLayer with a source array for client-side data.

The ArcGIS Maps SDK is free for non-commercial use and includes a generous free tier for commercial applications. An API key or OAuth token is required.

Google Maps JavaScript API

The Google Maps JavaScript API has built-in GeoJSON support through its Data layer. It integrates with Google's Places, Geocoding, Directions, and other services.

Loading GeoJSON

JavaScript (Google Maps)const map = new google.maps.Map(document.getElementById("map"), {
  center: { lat: 40.7, lng: -74.0 },
  zoom: 10
});

// Load from URL
map.data.loadGeoJson("/data/parks.geojson");

// Or add inline GeoJSON
map.data.addGeoJson(geojsonData);

Styling

JavaScriptmap.data.setStyle((feature) => ({
  fillColor: feature.getProperty("type") === "park" ? "#2d6a4f" : "#e63946",
  fillOpacity: 0.4,
  strokeColor: "#1b4332",
  strokeWeight: 2
}));

Click Events

JavaScript (Google Maps)const infoWindow = new google.maps.InfoWindow();

map.data.addListener("click", (event) => {
  const name = event.feature.getProperty("name");
  infoWindow.setContent(`<h3>${name}</h3>`);
  infoWindow.setPosition(event.latLng);
  infoWindow.open(map);
});

Pricing: Google Maps requires an API key and charges per map load after the free monthly credit ($200/month). The Data layer API works well for small to medium GeoJSON datasets.

Deck.gl

Deck.gl is a WebGL-powered framework designed for large-scale data visualization. Built by Vis.gl (formerly Uber's visualization team), it excels at rendering hundreds of thousands of features with GPU acceleration.

JavaScript (deck.gl)import { DeckGL } from "@deck.gl/react";
import { GeoJsonLayer } from "@deck.gl/layers";
import { Map } from "react-map-gl/maplibre";

function App() {
  const layer = new GeoJsonLayer({
    id: "parks",
    data: "/data/parks.geojson",
    filled: true,
    getFillColor: [45, 106, 79, 100],
    getLineColor: [27, 67, 50],
    getLineWidth: 2,
    pickable: true
  });

  return (
    <DeckGL
      initialViewState={{ longitude: -74, latitude: 40.7, zoom: 10 }}
      controller={true}
      layers={[layer]}
    >
      <Map mapStyle="https://demotiles.maplibre.org/style.json" />
    </DeckGL>
  );
}

Deck.gl is ideal when you need to visualize tens or hundreds of thousands of GeoJSON features without performance degradation. It pairs well with MapLibre or Mapbox as a basemap.

CesiumJS

CesiumJS is the leading open-source library for 3D globes and maps. It supports GeoJSON natively and is ideal for applications that need 3D terrain, time-dynamic data, or globe views.

JavaScript (CesiumJS)import { Viewer, GeoJsonDataSource } from "cesium";

const viewer = new Viewer("cesiumContainer");

GeoJsonDataSource.load("/data/parks.geojson", {
  fill: Cesium.Color.fromCssColorString("#2d6a4f").withAlpha(0.4),
  stroke: Cesium.Color.fromCssColorString("#1b4332"),
  strokeWidth: 2
}).then((dataSource) => {
  viewer.dataSources.add(dataSource);
  viewer.zoomTo(dataSource);
});

Performance Tips

  • Simplify geometries — use our GeoJSON Simplifier to reduce vertex counts before loading into the browser.
  • Reduce coordinate precision — 6 decimal places (~0.1m accuracy) is more than enough for web maps. Use our Coordinate Precision Tool.
  • Use vector tiles for large datasets — for datasets over 5–10 MB, convert to vector tiles (Mapbox/MapLibre) or use server-side rendering (GeoServer, ArcGIS).
  • Cluster point data — Mapbox GL JS, MapLibre, Leaflet (with plugins), and ArcGIS all support point clustering to improve rendering speed and readability.
  • Load data lazily — fetch GeoJSON only when the user pans/zooms to the relevant area, rather than loading everything upfront.
  • Use WebGL-based libraries for large datasets — if you have more than a few thousand features, choose Mapbox GL JS, MapLibre, Deck.gl, or ArcGIS over SVG-based Leaflet.

Framework Integration

Most mapping libraries work with React, Vue, Angular, and other frameworks through wrapper packages:

LibraryReactVueAngular
Leafletreact-leaflet@vue-leaflet/vue-leafletngx-leaflet
Mapbox GL JSreact-map-glvue-mapboxngx-mapbox-gl
MapLibrereact-map-gl/maplibrevue-maplibre-glngx-maplibre-gl
ArcGIS@arcgis/map-components@arcgis/map-components@arcgis/map-components
Google Maps@vis.gl/react-google-mapsvue3-google-map@angular/google-maps
Deck.gl@deck.gl/reactUse imperative APIUse imperative API

Further Reading