Route from A to B
Calculate and display driving routes Each library has its own approach: here's how they compare.
Quick Recommendations
Leaflet — The right choice when you want a ready-made routing UI with turn-by-turn instructions via the Leaflet Routing Machine plugin.
MapLibre GL JS — Makes the most sense when you need full control over route line styling with paint expressions and want to integrate routing into a data-driven map.
OpenLayers — A strong fit when routing must integrate with existing OGC/vector feature workflows and you need precise geometry operations on the route.
At a Glance
| Leaflet | MapLibre | OpenLayers | |
|---|---|---|---|
| Coordinates | [lat, lng] | [lng, lat] | fromLonLat([lng, lat]) |
| Rendering | DOM + Canvas (2D) | WebGL (GPU, 60fps) | Canvas + WebGL |
| Bundle size | ~42 KB gzipped | ~250 KB gzipped | ~290 KB gzipped (tree-shakeable) |
| Learning curve | Easiest (simple API, great tutorials) | Moderate (source/layer model, expressions) | Steepest (projections, OGC standards, verbose API) |
| 3D support | No native 3D (2D only) | Full: terrain, fill-extrusion, globe, sky | Limited: WebGL layers, no native globe |
| Code lines | 13 | 23 | 27 |
Implementation Details
| Leaflet | MapLibre | OpenLayers | |
|---|---|---|---|
| Routing Engine | OSRM (external): MapTiler has no routing API | OSRM (external): route rendered as GeoJSON line layer | OSRM (external): route as vector feature |
Code Snippets
Click a library to expand its code. Each snippet shows only the core logic: the scaffold handles HTML boilerplate.
Leaflet13 linesShow code
const map = L.map('map').setView([48.86, 2.35], 12);
L.tileLayer('https://api.maptiler.com/maps/streets-v4/256/{z}/{x}/{y}.png?key=YOUR_MAPTILER_KEY', {
tileSize: 256,
attribution: '© <a href="https://www.maptiler.com/">MapTiler</a> © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap contributors</a>'
}).addTo(map);
L.marker([48.8566, 2.3522]).addTo(map).bindPopup('Start: Paris Center');
L.marker([48.8738, 2.2950]).addTo(map).bindPopup('End: Arc de Triomphe');
fetch('https://router.project-osrm.org/route/v1/driving/2.3522,48.8566;2.2950,48.8738?overview=full&geometries=geojson')
.then(r => r.json())
.then(data => {
const coords = data.routes[0].geometry.coordinates.map(c => [c[1], c[0]]);
L.polyline(coords, { color: '#2196F3', weight: 5, opacity: 0.8 }).addTo(map);
});MapLibre GL JS23 linesShow code
const map = new maplibregl.Map({
container: 'map',
style: 'https://api.maptiler.com/maps/streets-v4/style.json?key=YOUR_MAPTILER_KEY',
center: [2.35, 48.86],
zoom: 12
});
map.addControl(new maplibregl.NavigationControl());
const start = [2.3364, 48.8606], end = [2.2945, 48.8584];
map.on('load', async () => {
const url = `https://router.project-osrm.org/route/v1/driving/${start[0]},${start[1]};${end[0]},${end[1]}?overview=full&geometries=geojson`;
const res = await fetch(url);
const data = await res.json();
if (!data.routes.length) return;
map.addSource('route', { type: 'geojson', data: { type: 'Feature', geometry: data.routes[0].geometry } });
map.addLayer({ id: 'route-line', type: 'line', source: 'route',
layout: { 'line-join': 'round', 'line-cap': 'round' },
paint: { 'line-color': '#2196F3', 'line-width': 5 } });
new maplibregl.Marker({ color: '#4CAF50' }).setLngLat(start).setPopup(new maplibregl.Popup().setText('Start: Louvre')).addTo(map);
new maplibregl.Marker({ color: '#F44336' }).setLngLat(end).setPopup(new maplibregl.Popup().setText('End: Eiffel Tower')).addTo(map);
const coords = data.routes[0].geometry.coordinates;
const bounds = coords.reduce((b, c) => b.extend(c), new maplibregl.LngLatBounds(coords[0], coords[0]));
map.fitBounds(bounds, { padding: 60 });
});OpenLayers27 linesShow code
var startCoord = [2.3522, 48.8566];
var endCoord = [2.2945, 48.8584];
var markerLayer = new ol.layer.Vector({
source: new ol.source.Vector({
features: [
new ol.Feature({ geometry: new ol.geom.Point(ol.proj.fromLonLat(startCoord)) }),
new ol.Feature({ geometry: new ol.geom.Point(ol.proj.fromLonLat(endCoord)) })
]
}),
style: new ol.style.Style({ image: new ol.style.Circle({ radius: 8, fill: new ol.style.Fill({ color: '#e63946' }), stroke: new ol.style.Stroke({ color: '#fff', width: 2 }) }) })
});
var routeLayer = new ol.layer.Vector({ source: new ol.source.Vector(), style: new ol.style.Style({ stroke: new ol.style.Stroke({ color: '#457b9d', width: 5 }) }) });
var map = new ol.Map({
target: 'map',
layers: [
new ol.layer.Tile({ source: new ol.source.XYZ({ url: 'https://api.maptiler.com/maps/streets-v4/256/{z}/{x}/{y}.png?key=YOUR_MAPTILER_KEY', attributions: '© <a href="https://www.maptiler.com/copyright/">MapTiler</a>' }) }),
routeLayer, markerLayer
],
view: new ol.View({ center: ol.proj.fromLonLat([2.32, 48.858]), zoom: 14 })
});
fetch('https://router.project-osrm.org/route/v1/driving/' + startCoord[0] + ',' + startCoord[1] + ';' + endCoord[0] + ',' + endCoord[1] + '?overview=full&geometries=geojson')
.then(function(r) { return r.json(); })
.then(function(data) {
var coords = data.routes[0].geometry;
var feature = new ol.format.GeoJSON().readFeature({ type: 'Feature', geometry: coords }, { featureProjection: 'EPSG:3857' });
routeLayer.getSource().addFeature(feature);
});Things to Know
Implementation-specific details that matter for route from a to b.
Leaflet Routing Machine handles OSRM API calls and renders a turn-by-turn instruction panel automatically; the route line uses L.polyline.
You must handle the OSRM fetch manually and add the decoded route geometry as a GeoJSON line layer; no built-in routing UI.
OSRM response is decoded and rendered as an ol.Feature with LineString geometry; no built-in routing panel.
Which Library to Use?
Display a driving route between two points on a Leaflet map using the OSRM routing API and L.polyline.
The right choice when you want a ready-made routing UI with turn-by-turn instructions via the Leaflet Routing Machine plugin.
Display a driving route between two points using the OSRM routing API and a GeoJSON line layer in MapLibre GL JS.
Makes the most sense when you need full control over route line styling with paint expressions and want to integrate routing into a data-driven map.
Display a driving route between two points using the OSRM routing API rendered as a vector feature on an OpenLayers map.
A strong fit when routing must integrate with existing OGC/vector feature workflows and you need precise geometry operations on the route.
Build Maps with AI
Generate code like these examples using mapping library skills in your IDE.