DEV Community

Cover image for How to Visualize and Style Routes on a MapLibre GL Map
Casey Rivers for Geoapify Maps API

Posted on

How to Visualize and Style Routes on a MapLibre GL Map

In a previous tutorial, we covered route visualization with Leaflet. Now let's explore how to achieve the same with MapLibre GL.

MapLibre GL offers powerful styling capabilities through its expression-based paint properties. Unlike raster-based libraries, MapLibre GL renders everything with WebGL, which means smooth zooming, rotation, and crisp lines at any scale.

The key difference from Leaflet: MapLibre GL uses layers and sources instead of direct GeoJSON rendering. This approach offers more flexibility - you can update styles without re-adding layers, and the GPU handles rendering efficiently.

This tutorial shows how to build a route visualization with interactive styling controls using MapLibre GL. You will learn to work with GeoJSON sources, line layers, and the Map Marker Icon API for custom markers.

APIs used:

What you will learn:

  • How to add GeoJSON sources and line layers in MapLibre GL
  • Creating route outlines with layer ordering
  • Using setPaintProperty for dynamic style updates
  • Building custom markers with HTML elements
  • Differences from Leaflet route visualization

Table of Contents

  1. Why MapLibre GL for Routes
  2. Set Up the Map
  3. Add Route Layers
  4. Update Styles Dynamically
  5. Create Custom Markers
  6. Build Interactive Controls
  7. MapLibre GL vs Leaflet: Key Differences
  8. Explore the Demo
  9. Summary
  10. FAQ

Why MapLibre GL for Routes

MapLibre GL brings several advantages for route visualization:

  • Vector rendering: Lines stay crisp at any zoom level
  • GPU acceleration: Smooth performance even with complex routes
  • Dynamic styling: Update colors and widths without re-rendering
  • Built-in line offset: Native support for parallel route lines (useful for multi-route displays)
  • 3D capabilities: Pitch and bearing for immersive views

The trade-off is a steeper learning curve. MapLibre GL uses a declarative approach where you define sources (data) and layers (how to render data) separately.

Screenshot showing a styled route on MapLibre GL with the control panel


Set Up the Map

MapLibre GL loads a complete style JSON instead of individual tile layers:

const map = new maplibregl.Map({
    container: "map",
    style: `https://maps.geoapify.com/v1/styles/osm-bright/style.json?apiKey=${API_KEY}`,
    center: [2.3376, 48.8606], // Paris [lng, lat]
    zoom: 13
});

map.addControl(new maplibregl.NavigationControl(), "bottom-right");
Enter fullscreen mode Exit fullscreen mode

Note the coordinate order: MapLibre GL uses [longitude, latitude] (GeoJSON standard), while Leaflet uses [latitude, longitude].

Key: Sign up at geoapify.com to get your API key.


Add Route Layers

In MapLibre GL, add a GeoJSON source first, then create layers that reference it:

map.addSource("route", {
    type: "geojson",
    data: routeData
});

// Outline layer (rendered first, appears below)
map.addLayer({
    id: "route-outline",
    type: "line",
    source: "route",
    layout: { "line-join": "round", "line-cap": "round" },
    paint: {
        "line-color": outlineColor,
        "line-width": routeWidth + outlineWidth * 2,
        "line-opacity": routeOpacity
    }
});

// Main route layer
map.addLayer({
    id: "route-line",
    type: "line",
    source: "route",
    layout: { "line-join": "round", "line-cap": "round" },
    paint: {
        "line-color": routeColor,
        "line-width": routeWidth,
        "line-opacity": routeOpacity
    }
});
Enter fullscreen mode Exit fullscreen mode

By adding the outline layer first, MapLibre GL draws it below the main route. The outline width is routeWidth + outlineWidth * 2 so outlineWidth pixels show on each side.

Here's how the route looks with and without outline:

Screenshot showing route with outline enabled and disabled


Update Styles Dynamically

MapLibre GL's setPaintProperty method changes styles instantly without re-adding layers:

map.setPaintProperty("route-line", "line-color", routeColor);
map.setPaintProperty("route-line", "line-width", routeWidth);
map.setPaintProperty("route-line", "line-opacity", routeOpacity);

map.setPaintProperty("route-outline", "line-color", outlineColor);
map.setPaintProperty("route-outline", "line-width", showOutline ? routeWidth + outlineWidth * 2 : 0);
Enter fullscreen mode Exit fullscreen mode

This is more efficient than Leaflet's approach. The GPU updates rendering without recreating geometry.


Create Custom Markers

MapLibre GL markers use HTML elements. Create a div with the marker image as background:

const el = document.createElement("div");
el.style.width = `${dimensions.width}px`;
el.style.height = `${dimensions.height}px`;
el.style.backgroundImage = `url(${iconUrl})`;
el.style.backgroundSize = "contain";

const marker = new maplibregl.Marker({element: el, anchor: dimensions.anchor})
    .setLngLat([wp.lon, wp.lat])
    .setPopup(popup)
    .addTo(map);
Enter fullscreen mode Exit fullscreen mode

The anchor property determines which part of the element sits on the coordinates:

  • "center": Element center on coordinates (good for circles)
  • "bottom": Bottom center on coordinates (good for pins)

Screenshot showing a custom marker on the map


Build Interactive Controls

Controls call updateRouteStyle() instead of re-rendering layers:

document.getElementById("route-color").addEventListener("input", (e) => {
    document.getElementById("route-color-value").textContent = e.target.value.toUpperCase();
    updateRouteStyle();
});

document.getElementById("marker-type").addEventListener("change", renderMarkers);
Enter fullscreen mode Exit fullscreen mode

The pattern: listen for input changes, update the displayed value, and call the appropriate update function.


MapLibre GL vs Leaflet: Key Differences

Aspect Leaflet MapLibre GL
Coordinate order [lat, lng] [lng, lat]
Adding routes L.geoJSON(data).addTo(map) Source + Layer
Style updates Remove and re-add layer setPaintProperty()
Layer ordering Custom panes Layer add order
Markers L.icon() with image URL HTML elements
Line offset Plugin required Built-in line-offset
Performance Good for simple routes Better for complex routes

When to use MapLibre GL:

  • Complex routes with many points
  • Need for 3D views (pitch/bearing)
  • Multiple overlapping routes (built-in line offset)
  • Frequent style updates
  • Vector tile base maps

When to use Leaflet:

  • Simpler learning curve
  • Existing Leaflet codebase
  • Raster tile base maps
  • Extensive plugin ecosystem

Explore the Demo

The interactive demo lets you experiment with all styling parameters in real-time. Try adjusting:

  • Route styling - color, width, and opacity
  • Outline effects - toggle, color, and width
  • Marker types - awesome pins, material pins, or circles
  • Marker size - from 32px to 80px
  • Shadow toggle - for markers

MapLibre GL's setPaintProperty() makes these updates instant without re-rendering the entire route. Play with different combinations to see how MapLibre GL handles dynamic styling.

👉 Try the live demo:


Summary

MapLibre GL provides powerful tools for route visualization with excellent performance and flexible styling. This tutorial covered:

  1. Sources and layers: The MapLibre GL approach to data and rendering
  2. Layer ordering: Outline below route by adding layers in order
  3. Dynamic updates: Using setPaintProperty() for efficient style changes
  4. HTML markers: Creating custom markers with the Map Marker Icon API
  5. Coordinate order: Remember [lng, lat] for MapLibre GL

Recommended settings:

  • Route width: 5-8 pixels
  • Outline width: 1-3 pixels (appears on each side)
  • Opacity: 0.8-1.0 for clear visibility
  • Marker size: 40-56 pixels

Key takeaways:

  • MapLibre GL uses sources (data) and layers (rendering) separately
  • setPaintProperty() updates styles without re-rendering
  • Layer order determines z-index (first added = bottom)
  • HTML markers offer full customization flexibility
  • Built-in line offset makes multi-route visualization easier

Useful links:


FAQ

Q: What's the main advantage of MapLibre GL over Leaflet for routes?

A: Dynamic style updates without re-rendering. With MapLibre GL, you can change colors, widths, and other properties instantly using setPaintProperty(). Leaflet requires removing and re-adding layers. This makes MapLibre GL better for interactive styling or real-time updates.

Q: Why does MapLibre GL use [lng, lat] instead of [lat, lng]?

A: MapLibre GL follows the GeoJSON specification, which uses [longitude, latitude] order. This is the international standard. Leaflet uses [lat, lng] for historical reasons and to match common geographic notation.

Q: Can I use raster tiles with MapLibre GL?

A: Yes, but it's not the primary use case. MapLibre GL is optimized for vector tiles. If you need raster tiles, Leaflet is often simpler. However, MapLibre GL can load raster sources if needed.

Q: How do I handle route clicks in MapLibre GL?

A: Use map.on('click', 'layer-id', function(e) { ... }) where 'layer-id' is your route layer ID. MapLibre GL's event system is layer-based rather than object-based like Leaflet.

Q: What's the performance difference for complex routes?

A: MapLibre GL uses GPU rendering, which handles complex geometries better than Leaflet's canvas/SVG approach. For routes with thousands of coordinate points or multiple routes, MapLibre GL typically performs better, especially at higher zoom levels.

Q: Can I animate routes with MapLibre GL?

A: Yes. You can animate by updating the source data progressively or using the line-dasharray property with animation. MapLibre GL's GPU rendering makes animations smoother than Leaflet for complex routes.

Q: How do I show multiple routes with different colors?

A: Add each route as a separate feature in the GeoJSON source, then use data-driven styling with expressions in the paint properties. For example: 'line-color': ['get', 'color'] where each feature has a 'color' property.


Try It Now

👉 Open the Live Demo

Sign up at geoapify.com to get your API key and start building beautifully styled routes with MapLibre GL.

Top comments (0)