How to Style GeoJSON Lines and Polygons
- API or service
- GeoJSON + JavaScript map libraries
- Task
- GeoJSON geometry → styled map layer
- Examples
- Difficulty
- Intermediate
- Time
- 10 min
You have GeoJSON lines or polygons—such as routes, service areas, administrative boundaries, or delivery zones—and need to control how they appear on an interactive map. The geometry remains the same across map libraries, but each renderer has its own styling API.
Keep visualization properties in application code or derive them from GeoJSON feature properties. This makes it possible to use a shared dataset while applying a library-specific line color, width, fill, opacity, or data-driven style.
Prepare the GeoJSON
GeoJSON coordinates always use [longitude, latitude] order. A polygon ring must repeat its first coordinate at the end so the ring is closed.
const geojson = {
type: "FeatureCollection",
features: [
{
type: "Feature",
properties: { kind: "route", color: "#1565c0" },
geometry: {
type: "LineString",
coordinates: [
[13.3777, 52.5163],
[13.3904, 52.5208],
[13.4010, 52.5169]
]
}
},
{
type: "Feature",
properties: { kind: "area", color: "#43a047" },
geometry: {
type: "Polygon",
coordinates: [[
[13.3900, 52.5140],
[13.4080, 52.5140],
[13.4080, 52.5230],
[13.3900, 52.5230],
[13.3900, 52.5140]
]]
}
}
]
};
The following implementations assume that the map and this geojson object already exist.
Style GeoJSON with Leaflet
L.geoJSON() converts the features into Leaflet vector layers. Its style callback runs for lines and polygons and can read each feature’s properties.
const geometryLayer = L.geoJSON(geojson, {
style(feature) {
const isPolygon = feature.geometry.type === "Polygon";
return {
color: feature.properties.color,
weight: isPolygon ? 2 : 5,
opacity: 0.9,
fillColor: feature.properties.color,
fillOpacity: isPolygon ? 0.25 : 0
};
}
}).addTo(map);
map.fitBounds(geometryLayer.getBounds(), { padding: [24, 24] });
Leaflet uses one path-style object for both geometry types. LineString features ignore fill properties, while Polygon features use both stroke and fill properties.
Style GeoJSON with MapLibre GL
Add the GeoJSON once, then render it through separate line and fill layers. The geometry filters ensure that each layer receives the appropriate feature type.
map.on("load", () => {
map.addSource("application-geometry", {
type: "geojson",
data: geojson
});
map.addLayer({
id: "application-areas",
type: "fill",
source: "application-geometry",
filter: ["==", ["geometry-type"], "Polygon"],
paint: {
"fill-color": ["get", "color"],
"fill-opacity": 0.25,
"fill-outline-color": ["get", "color"]
}
});
map.addLayer({
id: "application-lines",
type: "line",
source: "application-geometry",
filter: ["==", ["geometry-type"], "LineString"],
paint: {
"line-color": ["get", "color"],
"line-width": 5,
"line-opacity": 0.9
}
});
});
MapLibre expressions such as ["get", "color"] make the style data-driven. Use map.setPaintProperty() when the application needs to change a layer after it has been created.
Style GeoJSON with OpenLayers
Read the GeoJSON into the projection used by the map, then return an OpenLayers style based on the geometry and feature properties.
import GeoJSON from "ol/format/GeoJSON.js";
import VectorLayer from "ol/layer/Vector.js";
import VectorSource from "ol/source/Vector.js";
import Fill from "ol/style/Fill.js";
import Stroke from "ol/style/Stroke.js";
import Style from "ol/style/Style.js";
const source = new VectorSource({
features: new GeoJSON().readFeatures(geojson, {
featureProjection: "EPSG:3857"
})
});
const geometryLayer = new VectorLayer({
source,
style(feature) {
const color = feature.get("color");
const isPolygon = feature.getGeometry().getType() === "Polygon";
return new Style({
stroke: new Stroke({
color,
width: isPolygon ? 2 : 5
}),
fill: new Fill({
color: isPolygon ? `${color}40` : "transparent"
})
});
}
});
map.addLayer(geometryLayer);
map.getView().fit(source.getExtent(), { padding: [24, 24, 24, 24] });
The 40 suffix gives a six-digit hexadecimal color approximately 25% opacity. If your input already uses the map projection, omit featureProjection; standard GeoJSON normally uses geographic longitude and latitude.