Replace a label in the Mapbox Standard Style
This example demonstrates an advanced technique for replacing a single place label in the Mapbox Standard Style with custom text. Because the Standard style's internal layers are not directly editable, the technique works by importing Standard into a top-level style that adds a GeoJSON source with a replacement feature and a hard coded settlement-major-label layer.
The replacement feature's coordinates and properties must match those of the original feature in the place_label source layer of the Mapbox Streets tileset — fields like symbolrank, text_anchor, and capital control zoom visibility and symbol placement. The renderer's symbol placement engine suppresses the original label when the replacement occupies the same location.
The hard coded layer definition targets symbolrank=6, capital=0, lightPreset=day, and theme=default. If the Standard style's settlement-major-label layer changes in a future release, the layer definition will need to be updated to match.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Replace a label in the Mapbox Standard Style</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
<link href="https://api.mapbox.com/mapbox-gl-js/v3.32.0/mapbox-gl.css" rel="stylesheet">
<script src="https://api.mapbox.com/mapbox-gl-js/v3.32.0/mapbox-gl.js"></script>
<style>
body { margin: 0; padding: 0; }
#map { position: absolute; top: 0; bottom: 0; width: 100%; }
</style>
</head>
<body>
<div id="map"></div>
<script>
const map = new mapboxgl.Map({
// TO MAKE THE MAP APPEAR YOU MUST
// ADD YOUR ACCESS TOKEN FROM
// https://account.mapbox.com
accessToken: 'YOUR_MAPBOX_ACCESS_TOKEN',
container: 'map',
style: {
version: 8,
// The sprite and glyphs URLs must match the Standard style's assets
// so that dot icons and fonts used by settlement labels resolve correctly.
sprite: 'mapbox://sprites/mapbox/standard/7ixcpyhbhz67em71mmpln1klo',
glyphs: 'mapbox://fonts/mapbox/{fontstack}/{range}.pbf',
sources: {
// Replacement label feature. Coordinates and properties must match the
// original Streets tileset feature so the layer styles it correctly.
'nyc-label-point': {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [
-74.00596618652344, 40.71278477995827
]
},
properties: {
name: 'New Amsterdam',
name_en: 'New Amsterdam',
filterrank: 1,
class: 'settlement',
worldview: 'all',
symbolrank: 6,
text_anchor: 'left',
capital: 0
}
}
]
}
}
},
// Based on settlement-major-label from the Mapbox Standard style, simplified for a
// symbolrank=6, capital=0 settlement with lightPreset=day, theme=default:
// - All ["config", ...] expressions resolved to their default values
// - filter: symbolrank=6 passes z2–12 only, collapsed to a single step
// - text-size: symbolrank=6 always resolves the same branch at each zoom stop
// - text-font: ["concat", ["config", "font"], " Medium"] → "DIN Pro Medium"
// - text-color / text-halo-color: resolved from day lightPreset defaults
layers: [
{
id: 'settlement-major-label',
type: 'symbol',
source: 'nyc-label-point',
minzoom: 2,
maxzoom: 15,
filter: ['step', ['zoom'], false, 2, true, 13, false],
layout: {
'text-line-height': 1.1,
'text-size': [
'interpolate',
['cubic-bezier', 0.2, 0, 0.9, 1],
['zoom'],
3,
11,
6,
16,
8,
20,
15,
24
],
'text-radial-offset': ['step', ['zoom'], 0.55, 8, 0],
'symbol-sort-key': ['get', 'symbolrank'],
'icon-image': [
'step',
['zoom'],
[
'image',
'dot-11',
{
params: {
'color-primary': 'hsl(0, 0%, 0%)',
'color-secondary':
'hsla(0, 0%, 100%, 1)'
}
}
],
8,
''
],
'text-font': [
'DIN Pro Medium',
'Arial Unicode MS Bold'
],
'text-justify': [
'step',
['zoom'],
[
'match',
['get', 'text_anchor'],
['left', 'bottom-left', 'top-left'],
'left',
['right', 'bottom-right', 'top-right'],
'right',
'center'
],
8,
'center'
],
'text-anchor': [
'step',
['zoom'],
['get', 'text_anchor'],
8,
'center'
],
'text-field': [
'coalesce',
['get', 'name_en'],
['get', 'name']
],
'text-max-width': 7
},
paint: {
'text-opacity': [
'case',
['to-boolean', ['feature-state', 'hide']],
0,
1
],
'icon-opacity': [
'case',
['to-boolean', ['feature-state', 'hide']],
0,
1
],
'text-color': 'hsl(0, 0%, 0%)',
'text-halo-color': 'hsla(0, 0%, 100%, 1)',
'text-halo-width': 1,
'text-halo-blur': 1
}
}
],
// Import the Standard style as a basemap — this is what makes the technique work
imports: [{ id: 'basemap', url: 'mapbox://styles/mapbox/standard' }]
},
center: [-74.006, 40.7128],
zoom: 10
});
</script>
</body>
</html>
YOUR_MAPBOX_ACCESS_TOKEN with an access token from your Mapbox account.import React, { useEffect, useRef } from 'react';
import * as mapboxgl from 'mapbox-gl/esm';
import 'mapbox-gl/dist/mapbox-gl.css';
const MapboxExample = () => {
const mapContainerRef = useRef(null);
const mapRef = useRef(null);
useEffect(() => {
const map = (mapRef.current = new mapboxgl.Map({
// TO MAKE THE MAP APPEAR YOU MUST
// ADD YOUR ACCESS TOKEN FROM
// https://account.mapbox.com
accessToken: 'YOUR_MAPBOX_ACCESS_TOKEN',
container: mapContainerRef.current,
style: {
version: 8,
// The sprite and glyphs URLs must match the Standard style's assets
// so that dot icons and fonts used by settlement labels resolve correctly.
sprite: 'mapbox://sprites/mapbox/standard/7ixcpyhbhz67em71mmpln1klo',
glyphs: 'mapbox://fonts/mapbox/{fontstack}/{range}.pbf',
sources: {
// Replacement label feature. Coordinates and properties must match the
// original Streets tileset feature so the layer styles it correctly.
'nyc-label-point': {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [-74.00596618652344, 40.71278477995827]
},
properties: {
name: 'New Amsterdam',
name_en: 'New Amsterdam',
filterrank: 1,
class: 'settlement',
worldview: 'all',
symbolrank: 6,
text_anchor: 'left',
capital: 0
}
}
]
}
}
},
// Based on settlement-major-label from the Mapbox Standard style, simplified for a
// symbolrank=6, capital=0 settlement with lightPreset=day, theme=default:
// - All ["config", ...] expressions resolved to their default values
// - filter: symbolrank=6 passes z2–12 only, collapsed to a single step
// - text-size: symbolrank=6 always resolves the same branch at each zoom stop
// - text-font: ["concat", ["config", "font"], " Medium"] → "DIN Pro Medium"
// - text-color / text-halo-color: resolved from day lightPreset defaults
layers: [
{
id: 'settlement-major-label',
type: 'symbol',
source: 'nyc-label-point',
minzoom: 2,
maxzoom: 15,
filter: ['step', ['zoom'], false, 2, true, 13, false],
layout: {
'text-line-height': 1.1,
'text-size': [
'interpolate',
['cubic-bezier', 0.2, 0, 0.9, 1],
['zoom'],
3,
11,
6,
16,
8,
20,
15,
24
],
'text-radial-offset': ['step', ['zoom'], 0.55, 8, 0],
'symbol-sort-key': ['get', 'symbolrank'],
'icon-image': [
'step',
['zoom'],
[
'image',
'dot-11',
{
params: {
'color-primary': 'hsl(0, 0%, 0%)',
'color-secondary': 'hsla(0, 0%, 100%, 1)'
}
}
],
8,
''
],
'text-font': ['DIN Pro Medium', 'Arial Unicode MS Bold'],
'text-justify': [
'step',
['zoom'],
[
'match',
['get', 'text_anchor'],
['left', 'bottom-left', 'top-left'],
'left',
['right', 'bottom-right', 'top-right'],
'right',
'center'
],
8,
'center'
],
'text-anchor': [
'step',
['zoom'],
['get', 'text_anchor'],
8,
'center'
],
'text-field': ['coalesce', ['get', 'name_en'], ['get', 'name']],
'text-max-width': 7
},
paint: {
'text-opacity': [
'case',
['to-boolean', ['feature-state', 'hide']],
0,
1
],
'icon-opacity': [
'case',
['to-boolean', ['feature-state', 'hide']],
0,
1
],
'text-color': 'hsl(0, 0%, 0%)',
'text-halo-color': 'hsla(0, 0%, 100%, 1)',
'text-halo-width': 1,
'text-halo-blur': 1
}
}
],
// Import the Standard style as a basemap — this is what makes the technique work
imports: [{ id: 'basemap', url: 'mapbox://styles/mapbox/standard' }]
},
center: [-74.006, 40.7128],
zoom: 10
}));
return () => map.remove();
}, []);
return (
<div ref={mapContainerRef} style={{ width: '100%', height: '100%' }} />
);
};
export default MapboxExample;
YOUR_MAPBOX_ACCESS_TOKEN with an access token from your Mapbox account.