<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mapbox</title>
    <description>The latest articles on DEV Community by Mapbox (mapbox).</description>
    <link>https://dev.to/mapbox</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F230%2F827ddd41-6aca-479c-967d-6f8d16fed8a3.png</url>
      <title>DEV Community: Mapbox</title>
      <link>https://dev.to/mapbox</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mapbox"/>
    <language>en</language>
    <item>
      <title>Rebuilding geojson.io: A Look at the 2026 Overhaul</title>
      <dc:creator>Andrew Sepic</dc:creator>
      <pubDate>Mon, 10 Aug 2026 15:37:35 +0000</pubDate>
      <link>https://dev.to/mapbox/rebuilding-geojsonio-a-look-at-the-2026-rewrite-38eg</link>
      <guid>https://dev.to/mapbox/rebuilding-geojsonio-a-look-at-the-2026-rewrite-38eg</guid>
      <description>&lt;p&gt;If you've worked with spatial data on the web, you've probably used &lt;a href="https://geojson.io" rel="noopener noreferrer"&gt;geojson.io&lt;/a&gt;. Since &lt;a href="https://github.com/tmcw" rel="noopener noreferrer"&gt;Tom MacWright&lt;/a&gt; first built it in 2013, it's become one of those quietly indispensable tools in the mapping community — drop in a GeoJSON file, sketch a polygon, grab a quick bounding box, copy the coordinates back out. No GIS software, no setup, just a browser tab.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F178f1aeu0asjpz132m02.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F178f1aeu0asjpz132m02.png" alt=" " width="800" height="434"&gt;&lt;/a&gt; &lt;em&gt;geojson.io screenshot from October 2, 2015 - courtesy of &lt;a href="https://web.archive.org/web/20151002061533/http://geojson.io/" rel="noopener noreferrer"&gt;Wayback Machine&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This year we shipped the biggest change in the app's 13 year history: a full overhaul of the editor, cut over to production on June 1, 2026. Below is how we approached this, what changed, why, and what's shipped since.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why overhaul it
&lt;/h2&gt;

&lt;p&gt;geojson.io's core had aged in place for a while. Under the hood, it had already been through one generation of map tech — originally built on &lt;a href="https://leafletjs.com/" rel="noopener noreferrer"&gt;Leaflet&lt;/a&gt;-based Mapbox.js library, then refactored to &lt;a href="https://github.com/mapbox/mapbox-gl-js" rel="noopener noreferrer"&gt;Mapbox GL JS&lt;/a&gt; and &lt;a href="https://github.com/mapbox/mapbox-gl-draw" rel="noopener noreferrer"&gt;Mapbox GL Draw&lt;/a&gt; in 2022 — but the application itself never changed alongside it. The UI was built entirely with &lt;a href="https://d3js.org/" rel="noopener noreferrer"&gt;D3&lt;/a&gt;, using its selection and data-join API as a general application-construction tool rather than for what it's best known for: data visualization. D3 has no built-in component model or state management, so anything beyond simple DOM bindings meant hand-rolling patterns a modern framework gives you for free.&lt;/p&gt;

&lt;p&gt;Rather than start from the ground up, we forked &lt;a href="https://play.placemark.io/" rel="noopener noreferrer"&gt;Placemark Play&lt;/a&gt; — an open-source geojson editor, also built by Tom MacWright, with an editing model designed from the start around multiselect and structured data editing — and adapted it into geojson.io's new foundation.&lt;/p&gt;

&lt;h2&gt;
  
  
  How we approached it
&lt;/h2&gt;

&lt;p&gt;Rewriting a decade-old tool creates an obvious risk: breaking workflows people depend on in the name of modernization. So one guiding principle shaped the whole project — treat every piece of legacy functionality as a question, not a given. Is this still useful? If so, does it need to look the same, or can it be replicated in a different pattern that fits better with the new architecture? Some features carried over largely unchanged; others got rebuilt around Placemark's multiselect-first editing model because the old pattern didn't make sense once "selection" could mean more than one feature at a time. That same audit ran in both directions — Placemark Play came with plenty of its own functionality that didn't serve geojson.io's specific mission, so a fair amount of the fork was just as much about stripping code out as building on top of it.&lt;/p&gt;

&lt;p&gt;We also didn't cut over all at once. The update launched quietly at a parallel &lt;code&gt;/next&lt;/code&gt; path back in January 2026, running alongside the legacy app so real usage and feedback could shape it before it became the default. That turned into a long stretch of triaging GitHub issues and user reports — and a lot of it wasn't about missing features so much as discovering how people were actually using geojson.io, often in ways we hadn't designed for or could have imagined. Users had built workflows around edge cases and quirks of the old app, well outside anything we'd call the "intended" use case. Each one became a judgment call: is this worth supporting, or would chasing it add complexity and noise the core tool doesn't need? We're still working through some of those today.&lt;/p&gt;

&lt;p&gt;It's ended up being as much a product exercise as an engineering one — shaped by feedback from Mapbox teams using it internally, and by the broader open-source GIS/mapping community, who've been generous (and opinionated, in the best way) about what they need from a tool like this.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Placemark gave us
&lt;/h2&gt;

&lt;p&gt;Forking Placemark Play didn't just hand us a newer codebase — it came with capabilities legacy geojson.io never had, the kind of thing that would've been its own multi-month project to build from scratch. Multiselect and bulk editing meant selecting many features at once and updating their properties together, instead of one at a time. Real editing history — undo/redo, copy, duplicate — plus spatial and geometry operations. Improved data management, with a list view and table view alongside the map, name previews for features, and a feature editor that treats properties as structured data instead of a JSON blob you hand-edit. And a codebase built for the present — modern React patterns and TypeScript that are both easier for us to maintain and a lower bar for outside contributors.&lt;/p&gt;

&lt;h2&gt;
  
  
  The new stack
&lt;/h2&gt;

&lt;p&gt;The overhaul moved the app to React 18, Vite, and TypeScript. A few of the more interesting pieces under the hood:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A More Capable Editing Toolkit&lt;/strong&gt; All drawing and editing logic is custom, built around a &lt;code&gt;PMap&lt;/code&gt; class that wraps Mapbox GL JS (&lt;code&gt;v3.24.0&lt;/code&gt;) directly and layers a Deck.gl &lt;code&gt;MapboxOverlay&lt;/code&gt; ( (&lt;code&gt;@deck.gl/core&lt;/code&gt;, &lt;code&gt;@deck.gl/mapbox&lt;/code&gt;, v9) on top for vertex rendering, selection highlighting, and snapping— This keeps a clean separation of concerns: &lt;strong&gt;Mapbox GL JS&lt;/strong&gt; is responsible for rendering the app's GeoJSON data, while Deck.gl owns the editing UI — vertex handles, selection highlighting, and snapping.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State lives in &lt;a href="https://jotai.org/docs/core/atom" rel="noopener noreferrer"&gt;Jotai&lt;/a&gt; atoms&lt;/strong&gt;, with &lt;a href="https://jotai.org/docs/utilities/storage#atomwithstorage" rel="noopener noreferrer"&gt;&lt;code&gt;atomWithStorage&lt;/code&gt;&lt;/a&gt; things like camera position and UI preferences go straight to localStorage/sessionStorage, no separate persistence layer needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Heavy lifting happens off the main thread.&lt;/strong&gt; Format conversion and reprojection — parsing a Shapefile, converting a big XLSX, reprojecting coordinates — runs in a Web Worker, built natively by Vite's worker support, so importing a large file doesn't freeze the map or the UI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TypeScript earns its keep on the data model.&lt;/strong&gt; GeoJSON Features, Geometries, and FeatureCollections aren't just untyped JSON — they're well-defined union types, and typing them properly catches a real class of bugs (wrong geometry type, malformed coordinate arrays) at the boundary, before they ever hit the map.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What we built on top
&lt;/h2&gt;

&lt;p&gt;Placemark's foundation covered the editing and data model — what it didn't cover was the specific functionality legacy geojson.io users had come to expect: globe projection, custom raster layers, sharing, and a long tail of smaller behaviors. Closing those gaps, alongside genuinely new capability, has been most of the work since launch:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The geojson text editing panel (using &lt;a href="https://codemirror.net/" rel="noopener noreferrer"&gt;CodeMirror&lt;/a&gt;), was a core part of the legacy geojson.io and ensures that your geojson text is still front and center next to your map. This helps beginners understand the &lt;a href="https://geojson.org/" rel="noopener noreferrer"&gt;geojson specification&lt;/a&gt; and formatting and also allows for direct editing and syncing to app state.&lt;/li&gt;
&lt;li&gt;Moving the Feature Editor into it's own flexible dynamic pop up, giving full control over a wide array of data on the selected feature.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/mapbox/simplestyle-spec" rel="noopener noreferrer"&gt;Simple Style Spec&lt;/a&gt; support for Markers &amp;amp; Polygons — render and edit simple style spec  &lt;code&gt;marker-color&lt;/code&gt;, &lt;code&gt;marker-symbol&lt;/code&gt;, &lt;code&gt;fill-color&lt;/code&gt; and more.&lt;/li&gt;
&lt;li&gt;Custom raster layers, including WMS support and TMS tilesets with inverted Y coordinates, so you can bring your own basemap imagery.&lt;/li&gt;
&lt;li&gt;Globe projection and 3D, with a toggle between globe and mercator, plus pitch/rotation for tilted views — integrating globe projection with Deck.gl's still-experimental support was one of the trickier parity problems.&lt;/li&gt;
&lt;li&gt;Vertex snapping while dragging, so edited or drawn geometries snap cleanly to nearby features and vertices.&lt;/li&gt;
&lt;li&gt;Shareable URLs — features are zipped and base64-encoded into a link, so you can hand someone a working map of your data without a backend.&lt;/li&gt;
&lt;li&gt;A steady stream of smaller fixes: streamlined file import for unambiguous formats (GeoJSON, KML, GPX, Shapefile skip the dialog entirely now), mobile panel resizing and two-finger pan while drawing, and persisted camera state that survives a page reload.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's a lot of surface area for a tool that's supposed to stay simple, and keeping it feeling simple — while adding real capability underneath — has been the actual design challenge of this overhaul.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F09o9k4cjshow57209tm4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F09o9k4cjshow57209tm4.png" alt=" " width="800" height="436"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The new &lt;a href="https://geojson.io" rel="noopener noreferrer"&gt;geojson.io&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Still open source
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://geojson.io" rel="noopener noreferrer"&gt;geojson.io&lt;/a&gt; remains open source, and the overhaul hasn't changed that. If you hit a bug or have a feature request, &lt;a href="https://github.com/mapbox/geojson.io/issues" rel="noopener noreferrer"&gt;open an issue on&lt;br&gt;
GitHub&lt;/a&gt; —  and if you want to see where it's headed, the &lt;a href="https://github.com/mapbox/geojson.io/blob/main/CHANGELOG.md" rel="noopener noreferrer"&gt;full changelog&lt;/a&gt; is public too.&lt;/p&gt;

&lt;p&gt;Thanks to Tom MacWright for building geojson.io in the first place, and for Placemark Play, which gave this project most of its DNA.&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>opensource</category>
      <category>mapbox</category>
      <category>geospatial</category>
    </item>
    <item>
      <title>Understanding the `near` Parameter in Mapbox's Search Box API</title>
      <dc:creator>Erwin Soekianto</dc:creator>
      <pubDate>Fri, 07 Aug 2026 15:31:17 +0000</pubDate>
      <link>https://dev.to/mapbox/understanding-the-near-parameter-in-mapboxs-search-box-api-4jk7</link>
      <guid>https://dev.to/mapbox/understanding-the-near-parameter-in-mapboxs-search-box-api-4jk7</guid>
      <description>&lt;p&gt;AI assistants and conversational search are changing how users search for places. Instead of typing an address or dropping a pin, users increasingly ask questions like "Find coffee near the Eiffel Tower" or "Harold Chicken Shack in Chicago." The new &lt;strong&gt;&lt;code&gt;near&lt;/code&gt;&lt;/strong&gt; parameter in the &lt;a href="https://docs.mapbox.com/api/search/search-box/" rel="noopener noreferrer"&gt;Mapbox Search Box API&lt;/a&gt; — introduced alongside the new &lt;a href="https://www.mapbox.com/blog/introducing-the-mapbox-places-api" rel="noopener noreferrer"&gt;Mapbox Places API&lt;/a&gt; (announced July 27, 2026) — makes these natural-language searches easier to implement, by removing the need for a separate geocoding request before you can search.&lt;/p&gt;

&lt;p&gt;In this post, we'll break down what &lt;code&gt;near&lt;/code&gt; does, why it exists, and how it stacks up against the other location-narrowing parameters Search Box already offers: &lt;code&gt;proximity&lt;/code&gt; and &lt;code&gt;bbox&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem &lt;code&gt;near&lt;/code&gt; solves
&lt;/h2&gt;

&lt;p&gt;Traditionally, if you wanted to search for "Harold Chicken Shack" close to Chicago using the &lt;code&gt;/forward&lt;/code&gt; endpoint, you had two separate jobs to do:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Put the search term in &lt;code&gt;q&lt;/code&gt; — e.g. &lt;code&gt;q=Harold Chicken Shack&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Tell the API &lt;em&gt;where&lt;/em&gt; to look using &lt;a href="https://docs.mapbox.com/api/search/search-box/#search-request" rel="noopener noreferrer"&gt;&lt;code&gt;proximity&lt;/code&gt; or &lt;code&gt;bbox&lt;/code&gt;&lt;/a&gt; — which meant you needed actual coordinates or a bounding box, not just the word "Chicago"&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That works fine for map UIs where you already know the user's location or have a pin on a map. But it breaks down for &lt;strong&gt;AI-driven or chat-style search&lt;/strong&gt;, where a user might just type or say "Harold Chicken Shack in Chicago" — a name and a place, mixed together in plain English, with no coordinate pair in sight. That's the gap &lt;code&gt;near&lt;/code&gt; closes.&lt;/p&gt;

&lt;h2&gt;
  
  
  How &lt;code&gt;near&lt;/code&gt; works
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;near&lt;/code&gt; accepts either a place-name string (like &lt;code&gt;Chicago&lt;/code&gt;, or a broader anchor like &lt;code&gt;California&lt;/code&gt;) or an explicit coordinate pair — whichever you have on hand — alongside your search term in &lt;code&gt;q&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;near&lt;/code&gt; is a convenience layer.&lt;/strong&gt; When you pass a place name, it first resolves that name via the geocoder's own &lt;code&gt;/forward&lt;/code&gt;, then automatically applies the result to your Search Box query. If you pass coordinates directly, &lt;code&gt;near&lt;/code&gt; skips the geocoding step and applies them the same way.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The resolution follows a defined hierarchy based on how coarse or fine the anchor is:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;
&lt;code&gt;near&lt;/code&gt; value&lt;/th&gt;
&lt;th&gt;Resolves to&lt;/th&gt;
&lt;th&gt;Effect on &lt;code&gt;q&lt;/code&gt;
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Raw &lt;code&gt;lon,lat&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Parsed directly&lt;/td&gt;
&lt;td&gt;Proximity point&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Country (e.g. &lt;code&gt;United States&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Geocoded to a country&lt;/td&gt;
&lt;td&gt;Bounding box + country filter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Region / state (e.g. &lt;code&gt;California&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Geocoded to a region&lt;/td&gt;
&lt;td&gt;Bounding box&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Place / locality (e.g. &lt;code&gt;Chicago&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Geocoded to a place&lt;/td&gt;
&lt;td&gt;Proximity point&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Neighborhood / address / POI (e.g. &lt;code&gt;Eiffel Tower&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Geocoded to a fine feature&lt;/td&gt;
&lt;td&gt;Proximity point&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In other words: countries and regions/states constrain results by &lt;code&gt;bbox&lt;/code&gt;; everything finer than that (cities, neighborhoods, addresses, landmarks) biases results by &lt;code&gt;proximity&lt;/code&gt; — which is why a strongly relevant result just outside the anchor can still appear (this is intentional and desirable). Country-level anchors get one thing regions don't: on top of the bbox, near also applies the dedicated country parameter (an ISO 3166-1 alpha-2 code, like US).&lt;/p&gt;

&lt;p&gt;One more mechanical detail worth knowing: if you pass both &lt;code&gt;near&lt;/code&gt; and &lt;code&gt;proximity&lt;/code&gt; in the same request, &lt;code&gt;near&lt;/code&gt; takes precedence — it overrides &lt;code&gt;proximity&lt;/code&gt;, and promotes whatever &lt;code&gt;proximity&lt;/code&gt; value you'd set to &lt;code&gt;origin&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;Worth noting: passing something coarser, like &lt;code&gt;near=California&lt;/code&gt;, is broad enough to constrain results with a &lt;code&gt;bbox&lt;/code&gt; instead of just biasing them. Which behavior you get depends on how specific or coarse the anchor is, per the table above.&lt;/p&gt;

&lt;p&gt;Here's what that manual, two-step process actually looks like without &lt;code&gt;near&lt;/code&gt; — first geocoding "Chicago" via &lt;a href="https://docs.mapbox.com/api/search/geocoding/" rel="noopener noreferrer"&gt;Geocoding v6&lt;/a&gt; &lt;code&gt;/forward&lt;/code&gt;, then passing the resulting coordinates into Search Box as &lt;code&gt;proximity&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;# Step 1: geocode "Chicago" to get coordinates
https://api.mapbox.com/search/geocode/v6/forward?q=Chicago&amp;amp;access_token=YOUR_MAPBOX_ACCESS_TOKEN
# → returns coordinates around -87.6298,41.8781

# Step 2: pass those coordinates into Search Box as proximity
https://api.mapbox.com/search/searchbox/v1/forward?q=Harold%20Chicken%20Shack&amp;amp;proximity=-87.6298,41.8781&amp;amp;access_token=YOUR_MAPBOX_ACCESS_TOKEN
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;near&lt;/code&gt;, both of those become a single request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;https://api.mapbox.com/search/searchbox/v1/forward?q=Harold%20Chicken%20Shack&amp;amp;near=Chicago&amp;amp;access_token=YOUR_MAPBOX_ACCESS_TOKEN
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same result, one less network round trip.&lt;/p&gt;

&lt;p&gt;This is also the connective tissue between Search Box and the new &lt;strong&gt;Places API&lt;/strong&gt;. The typical flow now looks like:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Send a Search Box &lt;code&gt;/forward&lt;/code&gt; query that includes &lt;code&gt;near&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Get back a &lt;code&gt;mapbox_id&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Feed that ID into the Places API's Details endpoint to pull the full enriched record (hours, photos, busyness, attributes, etc.)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One more thing worth knowing: &lt;code&gt;near&lt;/code&gt; isn't limited to &lt;code&gt;/forward&lt;/code&gt; — it's also supported on &lt;code&gt;/suggest&lt;/code&gt; and &lt;code&gt;/category&lt;/code&gt; with identical resolution semantics (it's not available on &lt;code&gt;/retrieve&lt;/code&gt; or &lt;code&gt;/reverse&lt;/code&gt;). So this same pattern applies whether you're building autocomplete, one-shot search, or category browse.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where this fits in a real product
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foq65p22v07glf1lmbswo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foq65p22v07glf1lmbswo.png" alt="Two ways near gets used: structured UI vs conversational/AI input requiring upstream decomposition" width="800" height="418"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's worth being explicit about something the API itself doesn't tell you: &lt;code&gt;near&lt;/code&gt; doesn't do any natural-language parsing. It only resolves a location string (or coordinates) that's already been isolated — it doesn't pull "Harold Chicken Shack" and "Chicago" apart out of a single sentence for you. That separation has to happen &lt;em&gt;before&lt;/em&gt; you ever call the Search Box API, and whose job that is depends on where your input is coming from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Structured UI&lt;/strong&gt; — a traditional search box with a separate location field, a map pin, a "search near me" button, a saved address. Here you already have the two pieces separately (no parsing needed) — you just pass them straight into &lt;code&gt;q&lt;/code&gt; and &lt;code&gt;near&lt;/code&gt; as-is.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Conversational or AI-driven input&lt;/strong&gt; — a chatbot, voice assistant, or LLM-powered search bar where the user hands you one freeform sentence. Here, something upstream of your Search Box call — an LLM prompt, an NLU/intent-extraction step, or simpler pattern matching for known formats — has to do the decomposition shown below. &lt;code&gt;near&lt;/code&gt; doesn't replace that step; it's the thing you call &lt;em&gt;after&lt;/em&gt; it, so you're not also manually geocoding the location piece yourself.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Splitting a natural-language query into &lt;code&gt;q&lt;/code&gt; and &lt;code&gt;near&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;If the input you're working with is a single fused string — the kind an AI assistant or chat interface might hand you, like &lt;code&gt;"Loyola University Chicago, Chicago, IL, USA"&lt;/code&gt; — you'll need to decompose it yourself before calling the API. The rule of thumb: put the &lt;strong&gt;specific entity&lt;/strong&gt; (the POI, brand, or category) in &lt;code&gt;q&lt;/code&gt;, and put the &lt;strong&gt;administrative tail&lt;/strong&gt; (the part that only disambiguates &lt;em&gt;where&lt;/em&gt; — city, state, country) in &lt;code&gt;near&lt;/code&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Original string&lt;/th&gt;
&lt;th&gt;&lt;code&gt;q&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;near&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Black Hills National Forest, SD, USA&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Black Hills National Forest&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SD, USA&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Mount Hood, Oregon, USA&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Mount Hood&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Oregon, USA&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;coffee in Portland Oregon&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;coffee&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Portland Oregon&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The same decomposition applies on &lt;code&gt;/suggest&lt;/code&gt; (with a partial query in &lt;code&gt;q&lt;/code&gt; as the user types). On &lt;code&gt;/category&lt;/code&gt; there's no &lt;code&gt;q&lt;/code&gt; to decompose at all — the category lives in the path, and &lt;code&gt;near&lt;/code&gt; carries the location intent on its own.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;near&lt;/code&gt; vs. &lt;code&gt;proximity&lt;/code&gt; vs. &lt;code&gt;bbox&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Search Box already gave you several ways to make a search location-aware. Here's how &lt;code&gt;near&lt;/code&gt; fits alongside them.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;proximity&lt;/code&gt; — bias, don't restrict
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;proximity&lt;/code&gt; doesn't filter anything out. It &lt;strong&gt;biases ranking&lt;/strong&gt; so that results closer to a given point are favored, while still allowing results further away to appear. You supply it separately from your query, either as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ip&lt;/code&gt; (use the requester's IP-based location), or&lt;/li&gt;
&lt;li&gt;explicit &lt;code&gt;longitude,latitude&lt;/code&gt; coordinates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key distinction: &lt;code&gt;proximity&lt;/code&gt; needs you to already have coordinates. It says "rank things near &lt;em&gt;this point&lt;/em&gt;," where &lt;em&gt;this point&lt;/em&gt; is something your app already knows (user's GPS location, map center, etc.).&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;bbox&lt;/code&gt; — a hard rectangular filter
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;bbox&lt;/code&gt; is a strict &lt;strong&gt;inclusion filter&lt;/strong&gt;. Anything outside the box (&lt;code&gt;min_lon,min_lat,max_lon,max_lat&lt;/code&gt;) is excluded outright — it's not a ranking signal, it's a wall. Useful when you know a user is restricted to, say, a specific metro area or map viewport and you never want results from outside it.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;near&lt;/code&gt; — resolves to proximity or bbox, depending on the anchor
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;near&lt;/code&gt; accepts a place name or coordinates and resolves them internally into either a &lt;code&gt;proximity&lt;/code&gt; bias or a &lt;code&gt;bbox&lt;/code&gt; filter, depending on how coarse the anchor is: countries and regions/states constrain by &lt;code&gt;bbox&lt;/code&gt;, while places, neighborhoods, addresses, and POIs bias by &lt;code&gt;proximity&lt;/code&gt;. Either way, &lt;code&gt;near&lt;/code&gt; is saving you the manual geocoding lookup you'd otherwise need before calling &lt;code&gt;proximity&lt;/code&gt; or &lt;code&gt;bbox&lt;/code&gt; directly — and if you pass both &lt;code&gt;near&lt;/code&gt; and &lt;code&gt;proximity&lt;/code&gt; together, &lt;code&gt;near&lt;/code&gt; wins, with the old &lt;code&gt;proximity&lt;/code&gt; value shifted to &lt;code&gt;origin&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A sample request and response
&lt;/h2&gt;

&lt;p&gt;Here's a real request/response pair, tested directly against the live API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;https://api.mapbox.com/search/searchbox/v1/forward?q=Harold%20Chicken%20Shack&amp;amp;near=Chicago&amp;amp;access_token=YOUR_MAPBOX_ACCESS_TOKEN
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The (trimmed) response came back as a standard GeoJSON &lt;code&gt;FeatureCollection&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FeatureCollection"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"features"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Feature"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"geometry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"coordinates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;-87.84130128&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;41.86461828&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Point"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Harold's Chicken Shack"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"mapbox_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dXJuOm1ieHBvaToxMjI1MjBkMi02MzIyLTQzYjItYmUwMi00MzJhNjBiYzM3ZmU"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"feature_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"poi"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"full_address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"713 Roosevelt Rd, Maywood, Illinois 60153, United States"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"place_formatted"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Maywood, Illinois 60153, United States"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"poi_category"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"american restaurant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"food and drink"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"metadata"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"phone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"+17083566048"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"distance"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2688893&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Feature"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"geometry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"coordinates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;-87.56676837&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;41.76573045&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Point"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Harold's Chicken Shack"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"full_address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"7114 S Yates Blvd, Chicago, Illinois 60649, United States"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"place_formatted"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Chicago, Illinois 60649, United States"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"distance"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2709632&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Feature"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"geometry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"coordinates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;-87.62617297&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;41.87392112&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Point"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Harolds Chicken Shack"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"full_address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"612 S Wabash Ave, Chicago, Illinois 60605, United States"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"place_formatted"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Chicago, Illinois 60605, United States"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"brand"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Chicken Shack"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"brand_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"chicken_shack"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"distance"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2706643&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"attribution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"© 2026 Mapbox and its suppliers. All rights reserved."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(For reference: &lt;code&gt;distance&lt;/code&gt; is reported in meters — 2,688,893 m ≈ 1,671 mi, 2,709,632 m ≈ 1,684 mi, and 2,706,643 m ≈ 1,682 mi.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;All three full results came back within the Chicago metro area. &lt;code&gt;near=Chicago&lt;/code&gt; narrowed the search exactly as you'd want it to, with no out-of-area results.&lt;/p&gt;

&lt;p&gt;Even without an outlier to compare against, the &lt;code&gt;distance&lt;/code&gt; field is still worth flagging: despite all five results being genuinely local to Chicago, &lt;code&gt;distance&lt;/code&gt; came back around 2.69–2.71 million meters (roughly &lt;strong&gt;1,671–1,684 miles&lt;/strong&gt;) for every result. That's consistent with &lt;code&gt;distance&lt;/code&gt; being based on &lt;code&gt;proximity&lt;/code&gt;/IP-derived location rather than on &lt;code&gt;near&lt;/code&gt; — in this case, the default reference point is apparently nowhere near Chicago either. Don't read &lt;code&gt;distance&lt;/code&gt; as a signal for how well &lt;code&gt;near&lt;/code&gt; narrowed things — check &lt;code&gt;place_formatted&lt;/code&gt;/&lt;code&gt;context&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;Worth calling out too: &lt;code&gt;metadata&lt;/code&gt; here is richer than the base parameter reference currently documents. In practice it can include &lt;code&gt;phone&lt;/code&gt;, &lt;code&gt;website&lt;/code&gt;, and a full &lt;code&gt;open_hours.periods&lt;/code&gt; array broken down by day of week — more than the generic "additional metadata" description in the docs suggests. From here, you'd grab &lt;code&gt;mapbox_id&lt;/code&gt; and pass it straight to the Places API's Details endpoint for the even fuller enriched record.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;th&gt;Input format&lt;/th&gt;
&lt;th&gt;Filters or just biases?&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;proximity&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ranks closer results higher&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ip&lt;/code&gt; or &lt;code&gt;longitude,latitude&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Biases only&lt;/td&gt;
&lt;td&gt;Map-based apps that already know user location&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;bbox&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Restricts results to an area&lt;/td&gt;
&lt;td&gt;&lt;code&gt;minLon,minLat,maxLon,maxLat&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hard filter&lt;/td&gt;
&lt;td&gt;Viewport-limited or region-locked search&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;near&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Resolves a place name or coordinates, then applies them as &lt;code&gt;proximity&lt;/code&gt; (specific anchors) or &lt;code&gt;bbox&lt;/code&gt; (coarse anchors)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;string&lt;/code&gt; (place name) or coordinates, paired with &lt;code&gt;q&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Both — biases for specific anchors, filters for coarse ones&lt;/td&gt;
&lt;td&gt;Conversational or natural-language search where you don't want to run a separate geocoding call&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  When to reach for which
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Building a classic search box on a map where you already have coordinates ready for &lt;code&gt;proximity&lt;/code&gt; or &lt;code&gt;bbox&lt;/code&gt;? Those parameters take coordinates directly. &lt;code&gt;near&lt;/code&gt; also accepts coordinates and resolves them the same way, so either path works if that's what you have on hand.&lt;/li&gt;
&lt;li&gt;Need a hard boundary, like restricting to a delivery zone or map viewport? Use &lt;code&gt;bbox&lt;/code&gt; directly for full control, though a coarse &lt;code&gt;near&lt;/code&gt; value (like a country or state) can behave similarly.&lt;/li&gt;
&lt;li&gt;Have a search term and a place name — from a chat interface, voice input, or freeform text — and don't want to run a separate geocoding call yourself? That's what &lt;code&gt;near&lt;/code&gt; is built for.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For point-like anchors, &lt;code&gt;near&lt;/code&gt; produces the same ranking behavior as &lt;code&gt;proximity&lt;/code&gt; directly — it's a convenience layer, not a separate ranking engine. For coarse anchors, it can behave like &lt;code&gt;bbox&lt;/code&gt; instead. Either way, &lt;code&gt;near&lt;/code&gt; isn't computing something &lt;code&gt;proximity&lt;/code&gt;/&lt;code&gt;bbox&lt;/code&gt; couldn't already do on their own — it's resolving the anchor and choosing the right mechanism for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to expect at the edges
&lt;/h2&gt;

&lt;p&gt;A few error and fallback behaviors worth knowing before you ship this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;Response&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;near&lt;/code&gt; omitted or empty&lt;/td&gt;
&lt;td&gt;Falls back to a normal &lt;code&gt;q&lt;/code&gt; search with no anchor — still valid&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;near&lt;/code&gt; set to invalid coordinates&lt;/td&gt;
&lt;td&gt;&lt;code&gt;400 Bad Request&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;near&lt;/code&gt; text isn't geocodable&lt;/td&gt;
&lt;td&gt;Graceful fallback — anchor is left unchanged, &lt;code&gt;q&lt;/code&gt; still runs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Geocoder temporarily unavailable&lt;/td&gt;
&lt;td&gt;&lt;code&gt;500 Internal Server Error&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;It's also worth setting expectations on result quality: in internal testing across categories like national forests, universities, airports, and malls, most &lt;code&gt;near&lt;/code&gt;-anchored queries resolved to the correct region with the intended entity ranked at or near the top — misses were the exception rather than the rule. The residual misses tend to be ranking order (the right result shows up a few slots down) or narrow indexing gaps — not incorrect anchoring.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;near&lt;/code&gt; gives you a single parameter that resolves a place name or coordinates into either a &lt;code&gt;proximity&lt;/code&gt; bias or a &lt;code&gt;bbox&lt;/code&gt; filter, depending on how specific the anchor is — sparing you a separate geocoding call when all you have is a name or a rough location. It's available on &lt;code&gt;/forward&lt;/code&gt;, &lt;code&gt;/suggest&lt;/code&gt;, and &lt;code&gt;/category&lt;/code&gt;, so this pattern isn't limited to a single query style. &lt;code&gt;bbox&lt;/code&gt; and &lt;code&gt;proximity&lt;/code&gt; remain the direct, explicit tools for when you already know exactly which behavior you want.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ready to try it?&lt;/strong&gt; &lt;code&gt;near&lt;/code&gt; is available today in the Search Box API — check the &lt;a href="https://docs.mapbox.com/api/search/search-box/#search-request" rel="noopener noreferrer"&gt;live API reference&lt;/a&gt; for the latest parameter details, or test it directly against the &lt;code&gt;/forward&lt;/code&gt;, &lt;code&gt;/suggest&lt;/code&gt;, and &lt;code&gt;/category&lt;/code&gt; endpoints with your own queries. You can also try it in the &lt;a href="https://docs.mapbox.com/playground/search-box/forward-reverse/?q=harold+chicken+shack&amp;amp;near=chicago&amp;amp;endpoint=forward" rel="noopener noreferrer"&gt;Search Box Playground&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqvlgibh1nomrl3yxyzan.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqvlgibh1nomrl3yxyzan.png" alt="Search Box Playground showing a near query" width="800" height="323"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>mapbox</category>
      <category>api</category>
      <category>geospatial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Putting a Live Map Inside Your AI Agent's Chat: MCP Apps and render_map_tool</title>
      <dc:creator>Matthew Podwysocki</dc:creator>
      <pubDate>Thu, 06 Aug 2026 13:47:27 +0000</pubDate>
      <link>https://dev.to/mapbox/putting-a-live-map-inside-your-ai-agents-chat-mcp-apps-and-rendermaptool-mkm</link>
      <guid>https://dev.to/mapbox/putting-a-live-map-inside-your-ai-agents-chat-mcp-apps-and-rendermaptool-mkm</guid>
      <description>&lt;p&gt;If you have used an MCP server before, you know the pattern: the AI agent calls a tool, the tool returns some JSON, and the model reads that JSON back to you in a sentence. That works fine for a list of restaurant names. It falls apart the moment the data is inherently spatial. A route is not really a sentence. Neither is a set of delivery zones or a cluster of nearby coffee shops. You want to see it on a map.&lt;/p&gt;

&lt;p&gt;That is the gap MCP Apps closes, and it is what we built &lt;code&gt;render_map_tool&lt;/code&gt; around in the &lt;a href="https://github.com/mapbox/mcp-server" rel="noopener noreferrer"&gt;Mapbox MCP Server&lt;/a&gt;. This post walks through what MCP Apps actually is, why we ended up with one visualization tool instead of one per data type, and how the tool works whether you are chaining it off another Mapbox tool or feeding it your own GeoJSON from a completely unrelated system.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Want to try this yourself before reading further? Follow the &lt;a href="https://github.com/mapbox/mcp-server/blob/main/docs/hosted-mcp-guide.md" rel="noopener noreferrer"&gt;hosted MCP server user guide&lt;/a&gt; to connect your own client to the production endpoint.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Try it now
&lt;/h2&gt;

&lt;p&gt;The Mapbox MCP server is hosted and ready to use, no local install required. The production endpoint is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://mcp.mapbox.com/mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Claude Code, add it before starting a session:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add &lt;span class="nt"&gt;--transport&lt;/span&gt; http mapbox-mcp-production https://mcp.mapbox.com/mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every other major client (Claude Desktop, VS Code, Cursor, and more) has its own setup steps, all covered in the &lt;a href="https://github.com/mapbox/mcp-server/blob/main/docs/hosted-mcp-guide.md" rel="noopener noreferrer"&gt;hosted MCP server user guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A quick primer: MCP and MCP Apps
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://modelcontextprotocol.io" rel="noopener noreferrer"&gt;Model Context Protocol&lt;/a&gt; is the open standard that lets an AI client (Claude Desktop, Claude Code, VS Code with Copilot, and others) talk to external tools and data sources in a consistent way. An MCP server exposes tools, the client's model decides when to call them, and the results come back as text or structured JSON that the model reasons over.&lt;/p&gt;

&lt;p&gt;That is enough for most tools, but it has an obvious ceiling: everything ends up flattened into text for the model to summarize. &lt;a href="https://github.com/modelcontextprotocol/ext-apps" rel="noopener noreferrer"&gt;MCP Apps&lt;/a&gt; is an extension to that protocol for exactly the cases where flattening loses too much. Instead of returning only text, a tool can also point at a small, self-contained HTML application that the client renders directly inside the conversation, in a sandboxed iframe. The tool still returns normal MCP output for the model to reason about. The HTML panel is an additional, richer surface for the human on the other end.&lt;/p&gt;

&lt;p&gt;For a Mapbox MCP server, that is the natural place to put an actual map.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why one tool, not one map per tool
&lt;/h2&gt;

&lt;p&gt;Our first instinct was the obvious one: give &lt;code&gt;directions_tool&lt;/code&gt; its own map panel, give &lt;code&gt;isochrone_tool&lt;/code&gt; its own map panel, and so on. We built that, then threw it away, for two reasons that only became clear once we had multiple tools in play.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chain position matters more than you would expect.&lt;/strong&gt; A lot of real requests are two steps: get directions, then show them. That means two tool calls in sequence, and several MCP App hosts, Claude Desktop among them, only fully render the interactive panel for the &lt;em&gt;last&lt;/em&gt; tool call in a chain. If the map panel belongs to &lt;code&gt;directions_tool&lt;/code&gt; and something else runs after it, the panel either does not render or gets superseded. Funneling every visualization through one tool that is always the final step in the chain sidesteps the problem entirely, rather than working around it per tool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Geometry is expensive to pass through the model.&lt;/strong&gt; A route polyline or a set of isochrone contours can be tens of thousands of coordinate pairs. Making the model carry that as a tool-call argument on every hop is slow and burns tokens for no benefit, since the model never actually needs to reason about individual coordinates. We wanted a way to hand geometry from one tool to another without the model ever having to see it.&lt;/p&gt;

&lt;p&gt;The result is &lt;code&gt;render_map_tool&lt;/code&gt;: a single tool that is the only one in the server declaring an MCP Apps resource, and everything else that produces geospatial output hands its result to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Chaining: directions in, a live map out
&lt;/h2&gt;

&lt;p&gt;Here is what that looks like end to end. When you ask an agent for directions, &lt;code&gt;directions_tool&lt;/code&gt; returns the usual structured data (distance, duration, turn-by-turn instructions), plus one extra field:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"structuredContent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"routes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;*/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"mapboxRender"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"ref"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mapbox://selffetch/directions?data=..."&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;ref&lt;/code&gt; is a short, opaque reference string, not the route geometry itself. The model never sees a single coordinate. It just passes the ref straight through to &lt;code&gt;render_map_tool&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"payload_refs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"mapbox://selffetch/directions?data=..."&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can pass more than one ref in the same call, which is how you merge results from multiple tools onto a single map, for example an isochrone alongside a route:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"payload_refs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"mapbox://selffetch/isochrone?data=..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"mapbox://selffetch/directions?data=..."&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpqg0acut8bkike3zaioz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpqg0acut8bkike3zaioz.png" alt="Driving directions from the Golden Gate Bridge to Union Square rendered by render_map_tool, showing an 8.3 mile, ~24 minute route via US-101"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Driving from the Golden Gate Bridge to Union Square: about 8.3 miles, ~24 minutes via US-101 North/South, through a tunnel and a toll road, with light-to-moderate traffic along the way.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Under the hood, that &lt;code&gt;mapbox://selffetch/...&lt;/code&gt; ref is not a pointer into a server-side cache. It base64-encodes the original tool call's parameters directly. When the map panel loads in the client, it decodes the ref and fetches the route geometry itself, directly from the Mapbox Directions API, using the same public token already used to load map tiles. This turned out to matter in a way we did not fully appreciate until we hit it in practice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The map has to survive things the tool call does not.&lt;/strong&gt; A conversation can sit idle for an hour, the MCP server process can restart, or a client can reopen an old chat and re-render a map card that was created in a previous session. In every one of those cases, anything that depended on in-memory server state from the original request is gone. Early on, our refs pointed at exactly that kind of ephemeral store, and a restart quietly broke every map card that hadn't been viewed yet. Making the ref self-describing, and having the client fetch geometry itself rather than trust a stashed server-side result, fixes that categorically: there is no server memory to lose, so there is nothing to survive. It also means the map always reflects live data. If you ask for directions and check back on that same map card twenty minutes later, current traffic conditions are what gets drawn, not a snapshot from when the tool first ran.&lt;/p&gt;
&lt;h2&gt;
  
  
  Standalone: your own data, no other Mapbox tool required
&lt;/h2&gt;

&lt;p&gt;Chaining is what an LLM does automatically once it has called a Mapbox tool. But a lot of real usage has nothing to do with our tools at all. If you are integrating the Mapbox MCP server into your own agent and you already have geospatial data, your own delivery zones, a set of store locations from your own database, a GPS trace from a device you operate, there is no reason to route it through our APIs first just to get it on a map.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;render_map_tool&lt;/code&gt; accepts that data directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"summary"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Downtown delivery zone"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"layers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"delivery-zone"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fill"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Feature"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"geometry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Polygon"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"coordinates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;-122.4194&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;37.7749&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;-122.4094&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;37.7749&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;-122.4094&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;37.7849&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;-122.4194&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;37.7849&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;-122.4194&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;37.7749&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"paint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"fill-color"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#3b82f6"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"fill-opacity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"fill-outline-color"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#1d4ed8"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"markers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"coordinates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;-122.4144&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;37.7799&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"style"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"color"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#ef4444"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"popup"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Warehouse"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"legend"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Delivery zone"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"color"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#3b82f6"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"opacity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.25&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flp7zgngzhke8jmrordg5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flp7zgngzhke8jmrordg5.png" alt="A downtown delivery zone rendered as a blue fill polygon by render_map_tool, with a red warehouse pin at its center"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Rendered the downtown delivery zone map — the blue fill shows the zone with the warehouse pin at its center.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;No &lt;code&gt;payload_refs&lt;/code&gt;, no dependency on &lt;code&gt;directions_tool&lt;/code&gt; or any other tool in the server. This is the entire request, and it is not a lesser or fallback code path either. It is the same rendering pipeline every other tool in the server uses internally, just entered directly.&lt;/p&gt;

&lt;p&gt;A few things worth calling out for anyone integrating this from outside our tool set:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;layers&lt;/code&gt; and &lt;code&gt;markers&lt;/code&gt; are a thin pass-through to the Mapbox GL JS style spec&lt;/strong&gt;, not a custom DSL. The &lt;code&gt;paint&lt;/code&gt; and &lt;code&gt;layout&lt;/code&gt; objects go straight to &lt;code&gt;addLayer&lt;/code&gt;, so anything expressible in GL JS style is expressible in the payload. If you already know Mapbox GL JS, there is nothing new to learn here.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You do not need a Mapbox access token to build the payload.&lt;/strong&gt; Token handling for the actual map tiles happens entirely inside the client's rendering panel, using the server's own public-token resolution. Your &lt;code&gt;layers&lt;/code&gt;/&lt;code&gt;markers&lt;/code&gt; are plain GeoJSON and CSS-style values with nothing sensitive embedded in them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multiple layers and markers in one call are merged onto the same map&lt;/strong&gt;, and the camera auto-fits to everything drawn unless you pass an explicit &lt;code&gt;camera&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Each call is a fresh render.&lt;/strong&gt; The tool does not accumulate state across calls, so if you want several things on one map, put them in the same call's &lt;code&gt;layers&lt;/code&gt;/&lt;code&gt;markers&lt;/code&gt;/&lt;code&gt;payload_refs&lt;/code&gt; rather than calling the tool repeatedly and expecting it to build up a picture.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An LLM with access to the tool can also compose this JSON itself from a plain description, so a prompt like "show a fill polygon over these coordinates, with a red pin labeled Warehouse" works without you hand-building the payload at all. But the direct, tool-level integration is what matters if you are wiring this into an agent programmatically rather than relying on a model to fill it in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this fits if you cannot render MCP Apps at all
&lt;/h2&gt;

&lt;p&gt;Not every MCP client supports MCP Apps yet. In a client without support, &lt;code&gt;render_map_tool&lt;/code&gt; still returns its resolved payload as ordinary tool output. You lose the live interactive panel, but the call does not fail, and there is nothing to catch or work around on your end. If you specifically need a guaranteed visual regardless of client capability, the server also ships &lt;code&gt;static_map_image_tool&lt;/code&gt;, which returns a base64-encoded PNG that renders anywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;MCP Apps gives tools a way to hand a human a real, interactive surface instead of a wall of text. For anything spatial, that surface is a map. We built &lt;code&gt;render_map_tool&lt;/code&gt; as the one place all of that goes through in the Mapbox MCP server, whether the data came from chaining one of our own tools or from a GeoJSON payload you built entirely on your own. The chaining path keeps geometry out of the model's hands and off the wire until it is actually needed, and the standalone path means you never have to route your own data through us just to visualize it.&lt;/p&gt;

&lt;p&gt;If you want to see the whole payload schema, the compatible client list, or the reasoning behind specific design choices, the full guide is in the repo: &lt;a href="https://github.com/mapbox/mcp-server/blob/main/docs/render-map-tool.md" rel="noopener noreferrer"&gt;&lt;code&gt;docs/render-map-tool.md&lt;/code&gt;&lt;/a&gt;. The server itself is open source at &lt;a href="https://github.com/mapbox/mcp-server" rel="noopener noreferrer"&gt;github.com/mapbox/mcp-server&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ready to connect your own client? Follow the &lt;a href="https://github.com/mapbox/mcp-server/blob/main/docs/hosted-mcp-guide.md" rel="noopener noreferrer"&gt;hosted MCP server user guide&lt;/a&gt; to get started with the production endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build with Location AI
&lt;/h2&gt;

&lt;p&gt;Whether you're building AI agents, AI-native applications, or adding location intelligence to an existing product, check out the Mapbox &lt;a href="https://docs.mapbox.com/help/getting-started/location-ai/" rel="noopener noreferrer"&gt;Location AI getting started guides&lt;/a&gt; and try the companion benchmarks with your own data. If you'd like to discuss your use case, explore architectural approaches, or build together, &lt;a href="https://www.mapbox.com/location-ai/build" rel="noopener noreferrer"&gt;contact the Mapbox team&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>map</category>
      <category>agents</category>
    </item>
    <item>
      <title>Prompting for interactive web maps in 2026</title>
      <dc:creator>Chris Whong</dc:creator>
      <pubDate>Thu, 30 Jul 2026 20:01:17 +0000</pubDate>
      <link>https://dev.to/mapbox/prompting-for-interactive-web-maps-in-2026-2872</link>
      <guid>https://dev.to/mapbox/prompting-for-interactive-web-maps-in-2026-2872</guid>
      <description>&lt;p&gt;With the rise of AI coding tools, our approach to helping developers get started with Mapbox GL JS has had to adapt. These days you're probably building with &lt;a href="https://claude.com/product/claude-code" rel="noopener noreferrer"&gt;Claude Code&lt;/a&gt;, &lt;a href="https://chatgpt.com/codex/" rel="noopener noreferrer"&gt;Codex&lt;/a&gt;, or &lt;a href="https://codeassist.google/products/business" rel="noopener noreferrer"&gt;Gemini&lt;/a&gt; — and when it comes time to add a map to your project, being specific about the tools and technology you name in your prompt makes a bigger difference than you might expect.&lt;/p&gt;

&lt;p&gt;In this post, I'll start with a sample dataset and show you the real-world output of a few different prompts to Claude Code. The first prompt will be generic. The second will call for Mapbox products by name. The third will push further into custom UI. You'll see the difference immediately, and understand why all web maps are not created equal.&lt;/p&gt;

&lt;h2&gt;
  
  
  But first, GeoJSON data!
&lt;/h2&gt;

&lt;p&gt;For a sample dataset, I created a GeoJSON &lt;code&gt;FeatureCollection&lt;/code&gt; of three point features: landmark buildings in Manhattan, New York City. I've included properties like &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;description&lt;/code&gt;, and &lt;code&gt;imageUrl&lt;/code&gt; — the kind of thing we'd want to display to an interested map user. You can use &lt;a href="https://geojson.io" rel="noopener noreferrer"&gt;geojson.io&lt;/a&gt; to craft your own GeoJSON data when building spatial apps.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FeatureCollection"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"features"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Feature"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"geometry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Point"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"coordinates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;-73.9857&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;40.7484&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Empire State Building"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"An iconic 102-story Art Deco skyscraper completed in 1931. Once the tallest building in the world, it remains one of the most recognizable landmarks in New York City."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"yearBuilt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1931&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"imageUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://upload.wikimedia.org/wikipedia/commons/thumb/1/10/Empire_State_Building_%28aerial_view%29.jpg/500px-Empire_State_Building_%28aerial_view%29.jpg"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Feature"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"geometry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Point"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"coordinates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;-73.9755&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;40.7516&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Chrysler Building"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A stunning Art Deco masterpiece completed in 1930, famous for its terraced crown of radiating sunburst arches and gleaming stainless steel cladding."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"yearBuilt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1930&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"imageUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://upload.wikimedia.org/wikipedia/commons/thumb/7/76/Chrysler_Building_by_David_Shankbone_Retouched.jpg/500px-Chrysler_Building_by_David_Shankbone_Retouched.jpg"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Feature"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"geometry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Point"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"coordinates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;-73.9772&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;40.7527&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Grand Central Terminal"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A world-famous Beaux-Arts railroad terminal opened in 1913. Its Main Concourse features a celestial ceiling mural and massive arched windows."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"yearBuilt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1913&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"imageUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://upload.wikimedia.org/wikipedia/commons/thumb/7/71/Image-Grand_central_Station_Outside_Night_2.jpg/500px-Image-Grand_central_Station_Outside_Night_2.jpg"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhslcwvymvxjcxbihbnj0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhslcwvymvxjcxbihbnj0.png" alt="A GeoJSON FeatureCollection of three Manhattan landmarks displayed in a text editor" width="799" height="557"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The generic web map prompt
&lt;/h2&gt;

&lt;p&gt;For demonstration purposes, I'm using simple, single-file web pages with JS in a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag.&lt;/p&gt;

&lt;p&gt;If you write a prompt like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;In index.html, make an interactive map with
markers for each Feature in `landmarks.geojson`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might get a map like this:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/chris-whong/embed/xbgQWKe?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Without being told what to use, the AI coding tool reached for &lt;a href="https://leafletjs.com/" rel="noopener noreferrer"&gt;Leaflet&lt;/a&gt; and a free raster tileset published by &lt;a href="https://www.openstreetmap.org/#map=11/40.7868/-102.6899" rel="noopener noreferrer"&gt;OpenStreetMap&lt;/a&gt;. It did what you asked, and even went a step further by adding popups that appear on click. But the resulting map feels clunky, dated, and not like something you'd actually want to ship.&lt;/p&gt;

&lt;p&gt;Leaflet is a tried-and-true open source mapping library, but it comes from an era — over a decade ago — before vector-based maps came on the scene. It works by arranging PNG image tiles in a &lt;code&gt;div&lt;/code&gt;, with more PNGs or SVGs layered on top for markers and overlays. The basemap you see here is actually called &lt;a href="https://wiki.openstreetmap.org/wiki/Raster_tile_providers" rel="noopener noreferrer"&gt;"OpenStreetMap's Standard Tile Layer,"&lt;/a&gt; and it's intended as a reference basemap for people making edits to OpenStreetMap itself. Used as a basemap in front of real users, it's busy and distracting. It's not a bad tileset — it's just designed for a very different purpose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mapbox GL JS: ask for it by name!
&lt;/h2&gt;

&lt;p&gt;Now let's request the exact same map, but specify Mapbox products explicitly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;In index.html, add an interactive map with
markers for each feature in landmarks.geojson
using the latest version of Mapbox GL JS and
the Mapbox Standard Style.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the code is generated, your AI tool will remind you to add your &lt;a href="https://docs.mapbox.com/help/dive-deeper/access-tokens/" rel="noopener noreferrer"&gt;access token&lt;/a&gt;. Mapbox GL JS includes a generous &lt;a href="https://www.mapbox.com/pricing" rel="noopener noreferrer"&gt;free tier&lt;/a&gt; (50,000 map loads per month), but all usage needs to be tied to an account, so one small edit is required before the map will actually load:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Created index.html using Mapbox GL JS v3.27.0 with
the Standard style. You'll need to replace
YOUR_MAPBOX_ACCESS_TOKEN on line 30 with your
Mapbox access token for it to work.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll find the placeholder right before the call that instantiates the map. Swap in a token from your &lt;a href="https://account.mapbox.com/" rel="noopener noreferrer"&gt;Mapbox account page&lt;/a&gt; and you're ready to go:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;mapboxgl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accessToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR_MAPBOX_ACCESS_TOKEN&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;mapboxgl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;container&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;map&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mapbox://styles/mapbox/standard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;73.9800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;40.7505&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;zoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/chris-whong/embed/YPNRaQW?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;You'll immediately see — and feel — the difference. Scroll a bit and you'll experience smooth, incremental zooming. Basemap features fade in and out gradually, surfacing the most important information for whatever part of the planet you're looking at. Hold control and drag up and down, or left and right, and you can pitch and rotate the map, controlling the camera like you would in a video game.&lt;/p&gt;

&lt;p&gt;The two key things you asked for were &lt;a href="https://docs.mapbox.com/mapbox-gl-js/guides/" rel="noopener noreferrer"&gt;Mapbox GL JS&lt;/a&gt;, our best-in-class JavaScript mapping library, and the &lt;a href="https://www.mapbox.com/blog/standard-core-style" rel="noopener noreferrer"&gt;Mapbox Standard Style&lt;/a&gt;, our versatile, configurable 2D/3D basemap. The Standard Style is continuously updated with better data for the whole world, and our professional cartographers are constantly refining its design and usability.&lt;/p&gt;

&lt;p&gt;The basemap is worlds apart from where we started, but the markers and popups themselves are still fairly close to defaults. So let's go a step further with a prompt aimed squarely at the interaction design.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding UI/UX polish
&lt;/h2&gt;

&lt;p&gt;A classic marker-and-popup map may be all you need, but it can still feel generic. One more prompt gets you custom basemap configuration, camera moves, non-map UI elements, and dynamic markers, all working together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hide basemap Point of interest labels.

Use a circle layer instead of markers.

Add a symbol layer using label-bg.png as a
stretchable image to display a label above
each circle.

When clicked, animate the circle to a larger
radius, gray out or subdue the other features,
zoom and pitch the map to show the selected
feature, and animate an info panel in from the
bottom (image on the left, info on the right).
When the panel is closed, restore the original
camera view.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's a lot packed into that prompt, but take a look at the result first, and then I'll walk through each piece:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/chris-whong/embed/PwWxeaV?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;First we remove point of interest labels, one of many configurable properties of the Mapbox Standard Style. You can also modify individual colors of major features like water, land, or streets, hide 3d models, and apply new color themes. Explore all of the options in the &lt;a href="https://docs.mapbox.com/playground/standard-style/" rel="noopener noreferrer"&gt;Mapbox Standard Style Playground&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Instead of generic Mapbox GL JS markers, each point is now represented by a circle. More importantly, those circles live in a &lt;code&gt;circle&lt;/code&gt; layer — as opposed to markers, which are HTML elements positioned above the map — which makes them far more performant, since they're rendered directly by the WebGL engine that powers the map. This approach scales up to many thousands of features, and works with both GeoJSON and vector tile data sources.&lt;/p&gt;

&lt;p&gt;We also added a label to each feature, which is a nice touch that lets users know what the circle represents without exposing all of its underlying data up front. The label uses a &lt;code&gt;symbol&lt;/code&gt; layer with a stretchable image as its background. Like the circles, it's WebGL-rendered and will scale with whatever data you bring in.&lt;/p&gt;

&lt;p&gt;Here's &lt;code&gt;label-bg.png&lt;/code&gt;, the source image used for the label background. &lt;a href="https://docs.mapbox.com/mapbox-gl-js/example/add-image-stretchable/" rel="noopener noreferrer"&gt;Any image can be stretched this way&lt;/a&gt; — the stretchable regions are defined in code, so the label background expands cleanly no matter how long the text inside it is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;label-bg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;labelImg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;stretchX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
  &lt;span class="na"&gt;stretchY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1swfzftezlmsup6zjg5d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1swfzftezlmsup6zjg5d.png" alt="The label-bg.png source image with its stretchable content and edge regions marked" width="36" height="24"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The fun part happens when a circle is clicked:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Save the original camera view (center, zoom, pitch, bearing).&lt;/li&gt;
&lt;li&gt;Update the paint properties of the circle and label layers, enlarging the active feature and subduing the others.&lt;/li&gt;
&lt;li&gt;Populate the UI panel with data from the clicked feature.&lt;/li&gt;
&lt;li&gt;"Ease" the map into a nicely pitched view focused on the selected feature, showing 3D buildings with a smooth animation.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you want to go deeper, here's the code that fires on click — all of it happening inside one concise &lt;code&gt;openPanel()&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;openPanel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;coords&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Save original view on first activation&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;activeFeatureName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;originalView&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getCenter&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;zoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getZoom&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;pitch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPitch&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;bearing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBearing&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;activeFeatureName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Enlarge active circle, subdue inactive features&lt;/span&gt;
  &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setPaintProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;landmarks-circles&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;circle-radius&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;case&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;==&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;get&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;activeFeatureName&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;ACTIVE_RADIUS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;DEFAULT_RADIUS&lt;/span&gt;
  &lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setPaintProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;landmarks-circles&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;circle-color&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;case&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;==&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;get&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;activeFeatureName&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#3b82f6&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#b0b8c4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setPaintProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;landmarks-circles&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;circle-stroke-color&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;case&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;==&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;get&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;activeFeatureName&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#ffffff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#dde1e7&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setPaintProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;landmarks-circles&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;circle-opacity&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;case&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;==&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;get&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;activeFeatureName&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mf"&gt;0.5&lt;/span&gt;
  &lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setPaintProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;landmarks-circles&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;circle-stroke-opacity&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;case&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;==&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;get&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;activeFeatureName&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mf"&gt;0.4&lt;/span&gt;
  &lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setPaintProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;landmarks-labels&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text-opacity&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;case&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;==&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;get&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;activeFeatureName&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mf"&gt;0.3&lt;/span&gt;
  &lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setPaintProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;landmarks-labels&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;icon-opacity&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;case&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;==&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;get&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;activeFeatureName&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mf"&gt;0.3&lt;/span&gt;
  &lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="c1"&gt;// Update panel content&lt;/span&gt;
  &lt;span class="nx"&gt;panelImage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;imageUrl&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;panelImage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;imageUrl&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;block&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;panelName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;panelMeta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;yearBuilt&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`Built &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;yearBuilt&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;panelDesc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;panel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;open&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Zoom and pitch to feature&lt;/span&gt;
  &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;easeTo&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;coords&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;zoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;pitch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;800&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this prompt, we've gone from a fairly simple marker map to a genuinely compelling custom interaction. The underlying data hasn't changed at all — only the experience built on top of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why naming your tools matters
&lt;/h2&gt;

&lt;p&gt;AI coding assistants are only as specific as the prompts they're given. When you leave the choice of mapping library and basemap up to the model, it defaults to whatever shows up most often in its training data — which, for web maps, tends to be older, simpler, and less visually polished tooling. That's a reasonable default for a quick prototype, but it's rarely what you want for a production app.&lt;/p&gt;

&lt;p&gt;The fix is straightforward: treat your AI coding tool the way you'd treat a new contractor on your team. Tell it exactly which library, which version, and which style or theme you want, the same way you would in a technical spec or a code review comment. A few extra words in your prompt — "using the latest version of Mapbox GL JS and the Mapbox Standard Style" — is the difference between a map that feels like 2012 and one that feels like it belongs in your product today.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it yourself
&lt;/h2&gt;

&lt;p&gt;If you want to experiment with this on your own data:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Build a small GeoJSON file with &lt;a href="https://geojson.io" rel="noopener noreferrer"&gt;geojson.io&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Ask your AI coding tool for a map with no library specified, and see what you get.&lt;/li&gt;
&lt;li&gt;Re-run the prompt, this time naming &lt;a href="https://docs.mapbox.com/mapbox-gl-js/guides/" rel="noopener noreferrer"&gt;Mapbox GL JS&lt;/a&gt; and the &lt;a href="https://www.mapbox.com/blog/standard-core-style" rel="noopener noreferrer"&gt;Mapbox Standard Style&lt;/a&gt; explicitly.&lt;/li&gt;
&lt;li&gt;Push further with a UI/UX prompt of your own — try custom layers, click interactions, or animated camera moves.&lt;/li&gt;
&lt;li&gt;Compare the results — check out the &lt;a href="https://docs.mapbox.com/mapbox-gl-js/example/" rel="noopener noreferrer"&gt;Mapbox GL JS examples&lt;/a&gt; page for more patterns like clustering, custom markers, and popups you can ask your AI tool to replicate. Drop a link straight into your prompt, or, better yet, connect the &lt;a href="https://docs.mapbox.com/api/guides/docs-mcp-server/" rel="noopener noreferrer"&gt;Mapbox docs MCP Server&lt;/a&gt; so your coding tool can look up Mapbox APIs on its own.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once you see the difference side by side, "ask for it by name" becomes second nature — and it's a habit worth carrying into every prompt you write for a mapping feature, not just your first one.&lt;/p&gt;

</description>
      <category>vibecoding</category>
      <category>mapbox</category>
      <category>webmap</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Did You Know? Mapbox Geocoding API Lets You Filter by Feature Type</title>
      <dc:creator>Erwin Soekianto</dc:creator>
      <pubDate>Tue, 14 Jul 2026 17:04:57 +0000</pubDate>
      <link>https://dev.to/mapbox/did-you-know-mapbox-geocoding-api-lets-you-filter-by-feature-type-3oeb</link>
      <guid>https://dev.to/mapbox/did-you-know-mapbox-geocoding-api-lets-you-filter-by-feature-type-3oeb</guid>
      <description>&lt;p&gt;&lt;em&gt;Episode 1: Geocoding smarter, not harder&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This one's about the &lt;code&gt;types&lt;/code&gt; parameter on the &lt;strong&gt;&lt;a href="https://docs.mapbox.com/api/search/geocoding/#geographic-feature-types" rel="noopener noreferrer"&gt;Mapbox Geocoding API&lt;/a&gt;&lt;/strong&gt; specifically. (The &lt;a href="https://docs.mapbox.com/api/search/search-box/" rel="noopener noreferrer"&gt;Search Box API&lt;/a&gt; has its own &lt;code&gt;types&lt;/code&gt; parameter too, but that's a separate product — this post is just about Geocoding.)&lt;/p&gt;

&lt;p&gt;Say you're building a signup form and just need the &lt;strong&gt;city&lt;/strong&gt; someone typed. Or you're tagging support tickets by &lt;strong&gt;county&lt;/strong&gt; for routing. Or you just want the &lt;strong&gt;state&lt;/strong&gt; for a sales-tax lookup. You don't need a rooftop-accurate street address — but a lot of geocoders hand you the whole thing anyway, whether you asked for it or not.&lt;/p&gt;

&lt;p&gt;Turns out you can filter out the feature types that you don't need — Mapbox lets you ask for exactly the level you need.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; the &lt;code&gt;types&lt;/code&gt; param filters &lt;em&gt;which&lt;/em&gt; feature types come back in the results. Set &lt;code&gt;types=district&lt;/code&gt; and you get back only &lt;code&gt;district&lt;/code&gt;-type features. (&lt;code&gt;place&lt;/code&gt; = city, &lt;code&gt;district&lt;/code&gt; ≈ county in the US, &lt;code&gt;region&lt;/code&gt; = state, &lt;code&gt;postcode&lt;/code&gt; = ZIP). &lt;/p&gt;

&lt;h2&gt;
  
  
  Filtering with &lt;code&gt;types&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Here's a reverse geocode of a point in Manhattan with no &lt;code&gt;types&lt;/code&gt; filter — by default this can return separate address, place, region, and country features, all at once:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://api.mapbox.com/search/geocode/v6/reverse?
longitude=-74.05941186207377&amp;amp;
latitude=40.77969220566604&amp;amp;
access_token=TOKEN"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only need the zipcode for that same point? Filter it down:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://api.mapbox.com/search/geocode/v6/reverse?
longitude=-74.05941186207377&amp;amp;
latitude=40.77969220566604&amp;amp;
types=postcode&amp;amp;
access_token=TOKEN"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the &lt;code&gt;features&lt;/code&gt; array only contains &lt;code&gt;postcode&lt;/code&gt;-type results — something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FeatureCollection"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"features"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Feature"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dXJuOm1ieHBsYzpBU2VPN0E"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"geometry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Point"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"coordinates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;-74.056834&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;40.789203&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"mapbox_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dXJuOm1ieHBsYzpBU2VPN0E"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"feature_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"postcode"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"full_address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Secaucus, New Jersey 07094, United States"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"07094"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name_preferred"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"07094"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"coordinates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"longitude"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;-74.056834&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"latitude"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;40.789203&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"place_formatted"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Secaucus, New Jersey, United States"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"bbox"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;-74.094356&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;40.75046&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;-74.033648&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;40.807552&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"context"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"place"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"mapbox_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dXJuOm1ieHBsYzpFYmVvN0E"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Secaucus"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"wikidata_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Q1013249"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"district"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"mapbox_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dXJuOm1ieHBsYzpwQWJz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hudson County"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"wikidata_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Q490505"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"region"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"mapbox_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dXJuOm1ieHBsYzpBbVRz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"New Jersey"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"wikidata_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Q1408"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"region_code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NJ"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"region_code_full"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"US-NJ"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"mapbox_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dXJuOm1ieHBsYzpJdXc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"United States"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"wikidata_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Q30"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"country_code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"US"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"country_code_alpha_3"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USA"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"postcode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"mapbox_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dXJuOm1ieHBsYzpBU2VPN0E"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"07094"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"attribution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NOTICE: © 2026 Mapbox and its suppliers. All rights reserved. Use of this data is subject to the Mapbox Terms of Service (https://www.mapbox.com/about/maps/). This response and the information it contains may not be retained."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same idea for county — Mapbox's &lt;code&gt;district&lt;/code&gt; type is defined as "smaller than a region, larger than a place," and for US addresses, that maps to a county:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://api.mapbox.com/search/geocode/v6/reverse?
longitude=-74.05941186207377&amp;amp;
latitude=40.77969220566604&amp;amp;
types=district&amp;amp;
access_token=TOKEN"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same shape, just at the &lt;code&gt;district&lt;/code&gt; level:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FeatureCollection"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"features"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Feature"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dXJuOm1ieHBsYzpwQWJz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"geometry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Point"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"coordinates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;-74.04667&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;40.72069&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"mapbox_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dXJuOm1ieHBsYzpwQWJz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"feature_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"district"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"full_address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hudson County, New Jersey, United States"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hudson County"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name_preferred"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hudson County"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"coordinates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"longitude"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;-74.04667&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"latitude"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;40.72069&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"place_formatted"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"New Jersey, United States"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"bbox"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;-74.166087&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;40.641695&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;-73.98538&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;40.823543&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"context"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"region"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"mapbox_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dXJuOm1ieHBsYzpBbVRz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"New Jersey"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"wikidata_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Q1408"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"region_code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NJ"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"region_code_full"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"US-NJ"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"mapbox_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dXJuOm1ieHBsYzpJdXc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"United States"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"wikidata_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Q30"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"country_code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"US"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"country_code_alpha_3"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USA"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"district"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"mapbox_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dXJuOm1ieHBsYzpwQWJz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hudson County"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"wikidata_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Q490505"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"attribution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NOTICE: © 2026 Mapbox and its suppliers. All rights reserved. Use of this data is subject to the Mapbox Terms of Service (https://www.mapbox.com/about/maps/). This response and the information it contains may not be retained."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works the same way on forward geocoding, and there it also changes &lt;em&gt;which matches&lt;/em&gt; come back, not just filters a single result down. The docs' own &lt;a href="https://docs.mapbox.com/api/search/geocoding/#example-requests-forward-geocoding-with-search-text-input" rel="noopener noreferrer"&gt;example&lt;/a&gt;: searching &lt;code&gt;q=georgia&lt;/code&gt; with no filter can match both the country Georgia and the US state Georgia. Add &lt;code&gt;types=country&lt;/code&gt; and only the country comes back — you're excluding a whole category of match, not trimming one result's payload.&lt;/p&gt;

&lt;p&gt;You can also stack multiple types in one call, comma-separated — e.g. &lt;code&gt;types=address,street,place&lt;/code&gt; to get back any of those three, in one request.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it matters
&lt;/h2&gt;

&lt;p&gt;If you're doing this at volume — autocomplete-as-you-type, bulk address cleanup, routing tickets by county — asking for exactly the feature type(s) you need means fewer irrelevant features to filter out client-side, and no risk of grabbing the wrong one out of a mixed results list.&lt;/p&gt;

&lt;p&gt;Full list of feature types and how the &lt;code&gt;types&lt;/code&gt; param works: &lt;a href="https://docs.mapbox.com/api/search/geocoding/#geographic-feature-types" rel="noopener noreferrer"&gt;Mapbox Geocoding API docs&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Got a favorite obscure API parameter that saved you a bunch of overhead? Drop it in the comments — might be the next episode.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>mapbox</category>
      <category>api</category>
      <category>webdev</category>
      <category>geocoding</category>
    </item>
    <item>
      <title>Quick Tip: Dynamic Speed Units in Mapbox Navigation SDK for Android</title>
      <dc:creator>Erwin Soekianto</dc:creator>
      <pubDate>Wed, 17 Jun 2026 00:03:21 +0000</pubDate>
      <link>https://dev.to/mapbox/quick-tip-dynamic-speed-units-in-mapbox-navigation-sdk-for-android-4dm5</link>
      <guid>https://dev.to/mapbox/quick-tip-dynamic-speed-units-in-mapbox-navigation-sdk-for-android-4dm5</guid>
      <description>&lt;p&gt;Hi everyone! I’m Erwin, the newest &lt;strong&gt;Developer Evangelist&lt;/strong&gt; at &lt;strong&gt;Mapbox&lt;/strong&gt;. As I explore our SDKs, I’m focused on making our samples more flexible and easier to implement.&lt;/p&gt;

&lt;p&gt;Today, I’m sharing how to add a real-time MPH/KMH toggle to our &lt;a href="https://docs.mapbox.com/android/navigation/v2/examples/show-speed-limit/" rel="noopener noreferrer"&gt;Render speed limit for a route&lt;/a&gt; sample. This implementation is based on the &lt;a href="https://docs.mapbox.com/android/navigation/guides/localization/#set-speed-limit-units" rel="noopener noreferrer"&gt;Localization &amp;amp; internationalization guide&lt;/a&gt; in our docs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqy2czd0dfjlm71p8vo1q.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqy2czd0dfjlm71p8vo1q.gif" alt="See the unit in speed limit sign changing between mph and kph" width="800" height="1784"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Implementation
&lt;/h3&gt;

&lt;p&gt;In the Mapbox Navigation SDK for Android, the speed units are determined by the &lt;code&gt;unitType&lt;/code&gt; passed via &lt;code&gt;DistanceFormatterOptions&lt;/code&gt;. To make it dynamic, we cache the last location result to refresh the UI instantly.&lt;/p&gt;

&lt;p&gt;You can view the full source code changes for this enhancement in &lt;a href="https://github.com/mapbox/mapbox-navigation-android-examples/pull/222/changes/1a0cf0a16c64469a9176980a87bd6084386aeff3" rel="noopener noreferrer"&gt;this commit on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Updating the Layout
&lt;/h4&gt;

&lt;p&gt;First, we add a simple button to our layout to trigger the unit switch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;androidx.appcompat.widget.AppCompatButton&lt;/span&gt;
    &lt;span class="na"&gt;android:id=&lt;/span&gt;&lt;span class="s"&gt;"@+id/toggleUnitButton"&lt;/span&gt;
    &lt;span class="na"&gt;android:layout_width=&lt;/span&gt;&lt;span class="s"&gt;"wrap_content"&lt;/span&gt;
    &lt;span class="na"&gt;android:layout_height=&lt;/span&gt;&lt;span class="s"&gt;"wrap_content"&lt;/span&gt;
    &lt;span class="na"&gt;android:text=&lt;/span&gt;&lt;span class="s"&gt;"Toggle Unit"&lt;/span&gt;
    &lt;span class="na"&gt;app:layout_constraintEnd_toEndOf=&lt;/span&gt;&lt;span class="s"&gt;"parent"&lt;/span&gt;
    &lt;span class="na"&gt;app:layout_constraintTop_toTopOf=&lt;/span&gt;&lt;span class="s"&gt;"parent"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. The Toggle Logic
&lt;/h4&gt;

&lt;p&gt;Rebuild your options and trigger a refresh using the cached &lt;code&gt;LocationMatcherResult&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;lastLocationMatcherResult&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;LocationMatcherResult&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;

&lt;span class="n"&gt;binding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toggleUnitButton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setOnClickListener&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;newUnit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;distanceFormatterOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unitType&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="nc"&gt;UnitType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;METRIC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
                    &lt;span class="nc"&gt;UnitType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;IMPERIAL&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nc"&gt;UnitType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;METRIC&lt;/span&gt;

    &lt;span class="n"&gt;distanceFormatterOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;distanceFormatterOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unitType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newUnit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;// Immediate UI refresh&lt;/span&gt;
    &lt;span class="n"&gt;lastLocationMatcherResult&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;let&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;updateSpeedLimit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3. Updating the View
&lt;/h4&gt;

&lt;p&gt;Pass the updated options into the &lt;code&gt;MapboxSpeedInfoApi&lt;/code&gt; to get the correctly formatted speed "sign" data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;updateSpeedLimit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;locationMatcherResult&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;LocationMatcherResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;info&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;speedInfoApi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updatePostedAndCurrentSpeed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;locationMatcherResult&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;distanceFormatterOptions&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;binding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;speedLimitView&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isVisible&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;
        &lt;span class="n"&gt;binding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;speedLimitView&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;binding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;speedLimitView&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isVisible&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why it matters
&lt;/h3&gt;

&lt;p&gt;Localization isn't just a "set and forget" feature. Providing a seamless way to switch units enhances the driving experience for users crossing borders or those with specific personal preferences.&lt;br&gt;
For a deeper dive into localization, check out our full documentation &lt;a href="https://docs.mapbox.com/android/navigation/v2/guides/" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Expect more tips as I continue my journey with the Mapbox ecosystem!&lt;/p&gt;

</description>
      <category>mapbox</category>
      <category>androiddev</category>
      <category>kotlin</category>
      <category>devrel</category>
    </item>
    <item>
      <title>React + Mapbox GL JS: Custom Markers, Popups, and Bounds-Based Data Fetching</title>
      <dc:creator>Chris Whong</dc:creator>
      <pubDate>Wed, 03 Jun 2026 12:44:04 +0000</pubDate>
      <link>https://dev.to/mapbox/react-mapbox-gl-js-custom-markers-popups-and-bounds-based-data-fetching-4adj</link>
      <guid>https://dev.to/mapbox/react-mapbox-gl-js-custom-markers-popups-and-bounds-based-data-fetching-4adj</guid>
      <description>&lt;h1&gt;
  
  
  React + Mapbox GL JS: Custom Markers, Popups, and Bounds-Based Data Fetching
&lt;/h1&gt;

&lt;p&gt;If you've added a Mapbox map in a React app before, you've probably hit a wall pretty quickly: Mapbox GL JS manages its own DOM, and React manages a virtual DOM, and getting the two to play nicely takes some thought. Markers and popups are a great example of this tension — they're imperative APIs in a world where you want declarative components.&lt;/p&gt;

&lt;p&gt;This post walks through a pattern I've landed on for wrapping &lt;code&gt;mapboxgl.Marker&lt;/code&gt; and &lt;code&gt;mapboxgl.Popup&lt;/code&gt; in composable React components, and combining them with bounds-based data fetching to build something like a real estate or earthquake explorer map — where the data on the map updates as you pan and zoom.&lt;/p&gt;

&lt;p&gt;Here's what we'll cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wrapping &lt;code&gt;mapboxgl.Marker&lt;/code&gt; in a React component that handles its own lifecycle&lt;/li&gt;
&lt;li&gt;Using &lt;code&gt;createPortal&lt;/code&gt; to render custom marker content in React&lt;/li&gt;
&lt;li&gt;Fetching data from an API using the map's current bounding box&lt;/li&gt;
&lt;li&gt;Tracking an active marker with React state&lt;/li&gt;
&lt;li&gt;Wrapping &lt;code&gt;mapboxgl.Popup&lt;/code&gt; in a component&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to see the finished product first, the source code is &lt;a href="https://github.com/mapbox/tutorials/tree/main/dynamic-markers-react/" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Core Challenge: Two DOMs
&lt;/h2&gt;

&lt;p&gt;Before we get into the code, it's worth understanding &lt;em&gt;why&lt;/em&gt; this requires a pattern at all. When you use React's normal component tree, React controls the DOM. But &lt;code&gt;mapboxgl.Marker&lt;/code&gt; also creates and manages DOM nodes — it places an element on the map at a specific geographic coordinate, handles repositioning as you pan, and removes itself cleanly when you're done with it.&lt;/p&gt;

&lt;p&gt;The approach here is to use React to manage the &lt;em&gt;lifecycle&lt;/em&gt; of each marker (mount when data arrives, unmount when data goes away), while letting Mapbox handle the &lt;em&gt;positioning&lt;/em&gt;. We use React's &lt;code&gt;createPortal&lt;/code&gt; to render JSX content &lt;em&gt;into&lt;/em&gt; a DOM node that we hand off to Mapbox.&lt;/p&gt;

&lt;p&gt;The example discussed below is a map that fetches earthquake data for the current viewport, displaying a custom Marker and Popup for each. As you move the map and the bounds change, new data is fetched and the Markers and Popups are updated accordingly. Try it out:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/chris-whong/embed/yyVKeVb?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;




&lt;h2&gt;
  
  
  Setting Up: A Map Component That Fetches Data
&lt;/h2&gt;

&lt;p&gt;Start with a basic Mapbox GL JS map in a React component. The setup here is pretty standard — a &lt;code&gt;useRef&lt;/code&gt; for the map instance, a &lt;code&gt;useRef&lt;/code&gt; for the container element, and a &lt;code&gt;useEffect&lt;/code&gt; to initialize the map on mount and clean it up on unmount.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/Map.jsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useCallback&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;mapboxgl&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mapbox-gl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mapbox-gl/dist/mapbox-gl.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// mapRef holds the mapboxgl.Map instance so we can call methods on it&lt;/span&gt;
    &lt;span class="c1"&gt;// without triggering re-renders when it changes&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mapRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;// mapContainerRef points to the DOM element that Mapbox will render into&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mapContainerRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;// earthquakeData holds the GeoJSON FeatureCollection returned by the API&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;earthquakeData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setEarthquakeData&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;// useCallback prevents this function from being recreated on every render,&lt;/span&gt;
    &lt;span class="c1"&gt;// which matters because we pass it to map.on() event listeners&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getBboxAndFetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// getBounds() returns the lat/lng bounds of what's currently visible on screen&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bounds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mapRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBounds&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="c1"&gt;// Pass the SW and NE corners of the viewport as query params&lt;/span&gt;
                &lt;span class="c1"&gt;// so the API only returns earthquakes within the current view&lt;/span&gt;
                &lt;span class="s2"&gt;`https://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                &lt;span class="s2"&gt;`&amp;amp;starttime=2024-01-01&amp;amp;endtime=2024-01-30`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                &lt;span class="s2"&gt;`&amp;amp;minlatitude=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_sw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;maxlatitude=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_ne&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                &lt;span class="s2"&gt;`&amp;amp;minlongitude=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_sw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;maxlongitude=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_ne&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
            &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="nf"&gt;setEarthquakeData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;

    &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;mapRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;mapboxgl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR_MAPBOX_ACCESS_TOKEN&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;container&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;mapContainerRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;124&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;1.98&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  &lt;span class="c1"&gt;// Start centered on Indonesia, an earthquake-prone region&lt;/span&gt;
            &lt;span class="na"&gt;minZoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;5.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;          &lt;span class="c1"&gt;// Prevent zooming out too far — keeps API responses manageable&lt;/span&gt;
            &lt;span class="na"&gt;zoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;5.5&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;

        &lt;span class="c1"&gt;// Fetch once when the map finishes its initial load&lt;/span&gt;
        &lt;span class="nx"&gt;mapRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;load&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getBboxAndFetch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;// Fetch again whenever the user pans or zooms to a new area&lt;/span&gt;
        &lt;span class="nx"&gt;mapRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;moveend&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getBboxAndFetch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;// Clean up the map instance when this component unmounts&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;mapRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'map-container'&lt;/span&gt; &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;mapContainerRef&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key piece here is &lt;code&gt;getBboxAndFetch&lt;/code&gt;. It reads the map's current bounds using &lt;a href="https://docs.mapbox.com/mapbox-gl-js/api/map/#map#getbounds" rel="noopener noreferrer"&gt;&lt;code&gt;Map.getBounds()&lt;/code&gt;&lt;/a&gt;, constructs a URL to fetch earthquake data with those bounds as query parameters, and stores the response in state. It fires once on &lt;code&gt;load&lt;/code&gt; and again on every &lt;code&gt;moveend&lt;/code&gt; — so every time the user pans or zooms, new data comes in for all earthquakes that occurred in whatever part of the world is in view.&lt;/p&gt;

&lt;p&gt;I'm wrapping it in &lt;code&gt;useCallback&lt;/code&gt; to avoid it being recreated on every render, which would cause issues with the event listener setup.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Marker Component
&lt;/h2&gt;

&lt;p&gt;Here's where things get interesting. Rather than imperatively adding and removing markers in the &lt;code&gt;Map&lt;/code&gt; component, we can delegate that work to a dedicated &lt;code&gt;Marker&lt;/code&gt; component that handles its own lifecycle.&lt;/p&gt;

&lt;p&gt;The trick is using &lt;code&gt;createPortal&lt;/code&gt; to render JSX content into a DOM node that we create ourselves and hand to &lt;code&gt;mapboxgl.Marker&lt;/code&gt; as its element.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/Marker.jsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;mapboxgl&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mapbox-gl&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createPortal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-dom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Marker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isActive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;geometry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;properties&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;feature&lt;/span&gt;

    &lt;span class="c1"&gt;// markerRef holds the mapboxgl.Marker instance so we can remove it on unmount&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;markerRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;// contentRef is a plain DOM div that we create once — this is the bridge between&lt;/span&gt;
    &lt;span class="c1"&gt;// React's rendering and Mapbox's marker system. We hand it to Mapbox, and&lt;/span&gt;
    &lt;span class="c1"&gt;// React renders into it via createPortal below.&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;contentRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Pass contentRef.current as the element for the marker.&lt;/span&gt;
        &lt;span class="c1"&gt;// Mapbox will position this div on the map at the given coordinates.&lt;/span&gt;
        &lt;span class="nx"&gt;markerRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;mapboxgl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Marker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contentRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setLngLat&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;geometry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;coordinates&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;geometry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;coordinates&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;// Remove the marker from the map when this component unmounts.&lt;/span&gt;
        &lt;span class="c1"&gt;// This fires automatically when the parent re-renders with new data&lt;/span&gt;
        &lt;span class="c1"&gt;// and this earthquake feature is no longer in the result set.&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;markerRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;

    &lt;span class="c1"&gt;// createPortal renders our JSX into contentRef.current — the same div&lt;/span&gt;
    &lt;span class="c1"&gt;// that Mapbox is positioning on the map. React controls the content,&lt;/span&gt;
    &lt;span class="c1"&gt;// Mapbox controls the placement.&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;createPortal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;
            &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;inline-block&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2px 10px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;borderRadius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;50px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="c1"&gt;// Flip colors when this marker is the active (clicked) one&lt;/span&gt;
                &lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;isActive&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#333&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#fff&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;boxShadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0px 2px 4px rgba(0, 0, 0, 0.5)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;fontFamily&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Arial, sans-serif&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;fontSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;14px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;fontWeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bold&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;isActive&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#fff&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#333&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Show the earthquake's magnitude as the marker label */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mag&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;contentRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Marker&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break down what's happening:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;contentRef&lt;/code&gt; holds a plain DOM &lt;code&gt;div&lt;/code&gt; that we create once and never touch directly&lt;/li&gt;
&lt;li&gt;We pass that &lt;code&gt;div&lt;/code&gt; to &lt;code&gt;new mapboxgl.Marker(contentRef.current)&lt;/code&gt; — Mapbox takes ownership of positioning it on the map&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;createPortal&lt;/code&gt; renders our JSX &lt;em&gt;into&lt;/em&gt; that same &lt;code&gt;div&lt;/code&gt;, so React controls the content while Mapbox controls the placement&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;useEffect&lt;/code&gt; cleanup removes the marker from the map when the component unmounts&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The result is that you can build marker content using normal React patterns — JSX, props, conditional styles, event handlers — and it all "just works" inside Mapbox's marker system.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rendering Markers from Data
&lt;/h2&gt;

&lt;p&gt;Back in the &lt;code&gt;Map&lt;/code&gt; component, rendering markers is now straightforward. Map over the earthquake features, render a &lt;code&gt;Marker&lt;/code&gt; for each one, and use &lt;code&gt;feature.id&lt;/code&gt; as the React &lt;code&gt;key&lt;/code&gt;. That last part is important — when new data comes in after a pan, React will diff the keys and only create new &lt;code&gt;Marker&lt;/code&gt; instances for earthquakes that weren't in the previous result set.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// In Map.jsx&lt;/span&gt;

&lt;span class="c1"&gt;// activeFeature holds the full GeoJSON Feature of the currently selected marker,&lt;/span&gt;
&lt;span class="c1"&gt;// or undefined if no marker has been clicked yet&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;activeFeature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setActiveFeature&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;// Called by a Marker when it's clicked — stores the feature so we can&lt;/span&gt;
&lt;span class="c1"&gt;// highlight the active marker and position the popup&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleMarkerClick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setActiveFeature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* The div that Mapbox renders the map canvas into */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'map-container'&lt;/span&gt; &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;mapContainerRef&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;

        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Guard against mapRef.current being undefined on the first render,
            then render one Marker component per earthquake feature */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;mapRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;earthquakeData&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;features&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Marker&lt;/span&gt;
                &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;        &lt;span class="c1"&gt;// Stable ID lets React reuse existing markers on re-renders&lt;/span&gt;
                &lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;mapRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;    &lt;span class="c1"&gt;// The map instance, so Marker can call .addTo(map)&lt;/span&gt;
                &lt;span class="na"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;feature&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
                &lt;span class="na"&gt;isActive&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;activeFeature&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;  &lt;span class="c1"&gt;// True only for the clicked marker&lt;/span&gt;
                &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleMarkerClick&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;activeFeature&lt;/code&gt; stores the full feature object of the currently clicked marker. Passing &lt;code&gt;isActive={activeFeature?.id === feature.id}&lt;/code&gt; to each &lt;code&gt;Marker&lt;/code&gt; lets them style themselves differently when selected — in our case, flipping to a dark background and light text.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Popup Component
&lt;/h2&gt;

&lt;p&gt;Popups follow a similar pattern to markers. One difference is that we only ever need &lt;em&gt;one&lt;/em&gt; popup instance — it just moves around based on which marker is active.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/Popup.jsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createPortal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-dom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;mapboxgl&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mapbox-gl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Popup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;activeFeature&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// popupRef holds the single mapboxgl.Popup instance for its whole lifetime&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;popupRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;// contentRef is the same portal trick as in Marker — a plain DOM div&lt;/span&gt;
    &lt;span class="c1"&gt;// that React renders into, which we then pass to the Mapbox popup&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;contentRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c1"&gt;// Runs once on mount: create the popup instance but don't add it to the map yet.&lt;/span&gt;
    &lt;span class="c1"&gt;// We wait until activeFeature is set before positioning and showing it.&lt;/span&gt;
    &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="nx"&gt;popupRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;mapboxgl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Popup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;closeOnClick&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// Keep the popup open when the user clicks elsewhere on the map&lt;/span&gt;
            &lt;span class="na"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;            &lt;span class="c1"&gt;// Push the popup up slightly so it doesn't overlap the marker&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="c1"&gt;// Remove the popup from the map when this component unmounts&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;popupRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;

    &lt;span class="c1"&gt;// Runs whenever activeFeature changes: reposition the popup and update its content&lt;/span&gt;
    &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;activeFeature&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;

        &lt;span class="nx"&gt;popupRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setLngLat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;activeFeature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;geometry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;coordinates&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// Move popup to the clicked marker's location&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contentRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outerHTML&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;           &lt;span class="c1"&gt;// Serialize the React-rendered content to an HTML string&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                                      &lt;span class="c1"&gt;// Add (or re-add) the popup to the map&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;activeFeature&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="c1"&gt;// Render the popup content into contentRef via a portal.&lt;/span&gt;
    &lt;span class="c1"&gt;// By the time the second useEffect above reads contentRef.current.outerHTML,&lt;/span&gt;
    &lt;span class="c1"&gt;// React will have already rendered this JSX into the div.&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;createPortal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;table&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;tbody&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;strong&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Time&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;strong&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Convert the Unix timestamp (ms) to a human-readable local time */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
                        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;activeFeature&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toLocaleString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;strong&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Magnitude&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;strong&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;activeFeature&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mag&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;strong&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Place&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;strong&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;activeFeature&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;place&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;tbody&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;table&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;contentRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Popup&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are two &lt;code&gt;useEffect&lt;/code&gt; hooks here with different jobs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The first runs once on mount and creates the &lt;code&gt;mapboxgl.Popup&lt;/code&gt; instance (but doesn't add it to the map yet)&lt;/li&gt;
&lt;li&gt;The second runs whenever &lt;code&gt;activeFeature&lt;/code&gt; changes, and repositions the popup + updates its content&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One thing to note: &lt;code&gt;setHTML&lt;/code&gt; takes the &lt;code&gt;outerHTML&lt;/code&gt; of &lt;code&gt;contentRef.current&lt;/code&gt;, not a reference to the element itself. This is because Mapbox's popup API works with HTML strings for its content. The &lt;code&gt;createPortal&lt;/code&gt; has already rendered our JSX into that DOM node by the time this runs, so &lt;code&gt;outerHTML&lt;/code&gt; gives us the rendered output as a string.&lt;/p&gt;




&lt;h2&gt;
  
  
  Putting It All Together
&lt;/h2&gt;

&lt;p&gt;The final &lt;code&gt;Map&lt;/code&gt; component renders the map container, maps over the earthquake data to produce &lt;code&gt;Marker&lt;/code&gt; components, and includes a single &lt;code&gt;Popup&lt;/code&gt; that tracks the active feature:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// In Map.jsx&lt;/span&gt;

&lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* The map canvas container */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'map-container'&lt;/span&gt; &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;mapContainerRef&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;

        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* One Marker per earthquake — guarded so we don't render before the map exists */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;mapRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;earthquakeData&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;features&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Marker&lt;/span&gt;
                &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
                &lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;mapRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
                &lt;span class="na"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;feature&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
                &lt;span class="na"&gt;isActive&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;activeFeature&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
                &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleMarkerClick&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;

        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* A single Popup instance that repositions itself when activeFeature changes */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;mapRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Popup&lt;/span&gt; &lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;mapRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;activeFeature&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;activeFeature&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;mapRef.current &amp;amp;&amp;amp;&lt;/code&gt; guards are there because the map instance doesn't exist until after the first render — we don't want to pass &lt;code&gt;undefined&lt;/code&gt; as the &lt;code&gt;map&lt;/code&gt; prop.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where to Take It From Here
&lt;/h2&gt;

&lt;p&gt;This pattern is intentionally minimal. Some things you'd want to add in a real app:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Loading state&lt;/strong&gt;: Show a spinner or disable interaction while a fetch is in progress&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error handling&lt;/strong&gt;: Surface API errors in the UI instead of just logging them&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clustering&lt;/strong&gt;: Group nearby markers when zoomed out — Mapbox GL JS has good support for this if you switch to a GeoJSON source/layer approach&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your own data&lt;/strong&gt;: Swap out the USGS API for anything that returns GeoJSON and can be filtered by bounding box. This pattern works well for store locators, property listings, event maps, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The full source code for the earthquake demo is &lt;a href="https://github.com/mapbox/tutorials/tree/main/dynamic-markers-react/" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt; if you want to run it locally or use it as a starting point.&lt;/p&gt;

</description>
      <category>mapbox</category>
      <category>react</category>
      <category>javascript</category>
      <category>webmaps</category>
    </item>
    <item>
      <title>Demystifying Mapbox Styles</title>
      <dc:creator>Chris Whong</dc:creator>
      <pubDate>Tue, 05 May 2026 17:32:07 +0000</pubDate>
      <link>https://dev.to/mapbox/demystifying-mapbox-styles-2igc</link>
      <guid>https://dev.to/mapbox/demystifying-mapbox-styles-2igc</guid>
      <description>&lt;p&gt;If you've spent any time building digital maps with Mapbox, you've used a style — but you may not have given it much thought. Most tutorials and example code abstract it down to a single URL, and the map &lt;em&gt;just works&lt;/em&gt;. That's by design.&lt;/p&gt;

&lt;p&gt;But styles are doing a lot more than most developers realize, and understanding how they actually work opens up patterns that are useful whether you're just getting started or have been shipping Mapbox maps for years.&lt;/p&gt;

&lt;p&gt;This post pulls back the curtain: what a style really is, how it gets from a server to your screen, and a few tips and tricks for working with styles more intentionally.&lt;/p&gt;

&lt;h2&gt;
  
  
  What even is a style?
&lt;/h2&gt;

&lt;p&gt;When you load a map in a web or mobile app it is a &lt;code&gt;style&lt;/code&gt; that tells the map what to draw and how to draw it. The map rendering library (&lt;a href="https://docs.mapbox.com/mapbox-gl-js/guides/" rel="noopener noreferrer"&gt;Mapbox GL JS&lt;/a&gt;, or the Maps SDK for &lt;a href="https://docs.mapbox.com/ios/maps/guides/" rel="noopener noreferrer"&gt;iOS&lt;/a&gt;/&lt;a href="https://docs.mapbox.com/android/maps/guides/" rel="noopener noreferrer"&gt;Android&lt;/a&gt;/&lt;a href="https://docs.mapbox.com/flutter/maps/guides/" rel="noopener noreferrer"&gt;Flutter&lt;/a&gt;) loads and parses the style, then fetches whatever data is needed and renders it to the map canvas using GL technology. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F666y39qjncc37cftxkk3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F666y39qjncc37cftxkk3.png" alt=" " width="800" height="687"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.mapbox.com/map-styles/standard/guides/" rel="noopener noreferrer"&gt;Mapbox Standard Style&lt;/a&gt; is the default style for all renderers, but works in a slightly different way than our Classic Styles, but we'll get to that later. First, let's talk about how styles have worked historically. &lt;/p&gt;

&lt;p&gt;In the earlier days of Mapbox maps, most developers would start with one of Mapbox's &lt;a href="https://docs.mapbox.com/api/maps/styles/#classic-mapbox-styles" rel="noopener noreferrer"&gt;Classic Styles&lt;/a&gt;, a URL that looks like &lt;code&gt;mapbox://styles/mapbox/streets-v12&lt;/code&gt;. See all of the &lt;a href="https://docs.mapbox.com/api/maps/styles/#classic-mapbox-styles" rel="noopener noreferrer"&gt;Slassic Styles here&lt;/a&gt;. These styles were professionally designed by our cartographers, and purpose built for different use cases. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Streets&lt;/strong&gt; is a great all-purpose style, with a wide variety of colors and details. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Outdoors&lt;/strong&gt; is designed for hiking and outdoor activities, with terrain shading, contour lines, and vibrant colors. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Light&lt;/strong&gt; and &lt;strong&gt;Dark&lt;/strong&gt; are designed to be more subdued, ideal for maps where the overlaid data should be the star of the show, and the basemap should recede into the background. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With a &lt;code&gt;mapbox://&lt;/code&gt; URL to a classic style, a center point, and a zoom level, you can summon an amazingly detailed, fluidly scrolling map with five lines of code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;mapboxgl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mapbox://styles/mapbox/streets-v12&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;103.64548&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;51.14245&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;zoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.78&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/chris-whong/embed/pvNzPgO?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;What you may not know is that &lt;code&gt;mapbox://styles/mapbox/streets-v12&lt;/code&gt; is shorthand for a call to the &lt;a href="https://docs.mapbox.com/api/maps/styles/" rel="noopener noreferrer"&gt;Mapbox Styles API&lt;/a&gt;, whose sole purpose in life is (you guessed it) storing and serving up map styles! The actual API call is to &lt;code&gt;https://api.mapbox.com/styles/v1/mapbox/streets-v12&lt;/code&gt;, which returns the style as JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Mapbox Streets"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sprite"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mapbox://sprites/mapbox/streets-v12"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"glyphs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mapbox://fonts/mapbox/{fontstack}/{range}.pbf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"center"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="mf"&gt;-92.25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="mf"&gt;37.75&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"zoom"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"fog"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"projection"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"globe"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"visibility"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"public"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"layers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sources"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"composite"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mapbox://mapbox.mapbox-streets-v8,mapbox.mapbox-terrain-v2,mapbox.mapbox-bathymetry-v2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vector"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;  
  &lt;/span&gt;&lt;span class="nl"&gt;"created"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1970-01-01T00:00:00.000Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"modified"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1970-01-01T00:00:00.000Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"owner"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mapbox"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"streets-v12"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"draft"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;em&gt;actual&lt;/em&gt; JSON is about 13,000 lines long when pretty-printed, and 99% of it is in the &lt;code&gt;layers&lt;/code&gt; array. That's really where the action is, where every detail shown on the map is defined, often using complex &lt;a href="https://docs.mapbox.com/style-spec/reference/expressions/" rel="noopener noreferrer"&gt;expressions&lt;/a&gt; to handle zoom-dependent styling, multi-language support, etc. Here's just one, of no particular importance, so you can see what we're working with in the &lt;code&gt;layers&lt;/code&gt; array. &lt;code&gt;road-minor&lt;/code&gt; handles rendering of minor roads. It's a &lt;code&gt;line&lt;/code&gt; layer (other types include &lt;code&gt;fill&lt;/code&gt;, &lt;code&gt;circle&lt;/code&gt;, &lt;code&gt;symbol&lt;/code&gt;, etc) and pulls data from the &lt;code&gt;road&lt;/code&gt; source layer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Identifies this layer within the style.&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;road-minor&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

  &lt;span class="c1"&gt;// Tells the renderer to draw the features as lines.&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;line&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

  &lt;span class="c1"&gt;// Names the source dataset this layer reads from.&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;source&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;composite&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

  &lt;span class="c1"&gt;// Chooses the specific source layer inside the vector tileset.&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;source-layer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;road&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

  &lt;span class="c1"&gt;// Prevents the layer from appearing before zoom level 13.&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;minzoom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

  &lt;span class="c1"&gt;// Limits which road features are included in this layer.&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;filter&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;all&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;match&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;get&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;class&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;track&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;service&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;step&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;zoom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;match&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;get&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;structure&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ford&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;==&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;geometry-type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LineString&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;

  &lt;span class="c1"&gt;// Controls how line ends and corners are shaped.&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;layout&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;line-cap&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;step&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;zoom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;butt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;round&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;line-join&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;step&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;zoom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;miter&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;round&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="c1"&gt;// Defines the visible styling, including width and color.&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;paint&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;line-width&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;interpolate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;exponential&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.5&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;zoom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;line-color&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hsl(0, 0%, 100%)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="c1"&gt;// Stores internal grouping and categorization metadata.&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;metadata&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mapbox:featureComponent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;road-network&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mapbox:group&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Road network, surface&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All that just for one layer? Yes, and this is one of the simpler ones! &lt;/p&gt;

&lt;p&gt;You'll also notice that in the JSON for this style, there are even more magic &lt;code&gt;mapbox://&lt;/code&gt; URLs for the &lt;code&gt;sprite&lt;/code&gt; and &lt;code&gt;glyphs&lt;/code&gt;, and the &lt;code&gt;sources&lt;/code&gt;. &lt;a href="https://docs.mapbox.com/mapbox-gl-js/style-spec/sprite/" rel="noopener noreferrer"&gt;&lt;code&gt;sprite&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://docs.mapbox.com/mapbox-gl-js/style-spec/glyphs/" rel="noopener noreferrer"&gt;&lt;code&gt;glyphs&lt;/code&gt;&lt;/a&gt; are similar shorthand URLs for the styles API endpoints that serve additional resources needed to render the style. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;sprite&lt;/code&gt; gets back an composite image full of all those little icons you see on the map for restaurants, hotels, parks, attractions, etc. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;glyphs&lt;/code&gt; gets back font files that are used to render all the labels. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.mapbox.com/mapbox-gl-js/style-spec/sources/" rel="noopener noreferrer"&gt;&lt;code&gt;sources&lt;/code&gt;&lt;/a&gt; includes shorthand URLs for a different API, the &lt;a href="https://docs.mapbox.com/api/maps/vector-tiles/" rel="noopener noreferrer"&gt;Mapbox Vector Tiles API&lt;/a&gt;. These &lt;a href="https://docs.mapbox.com/data/tilesets/guides/vector-tiles-introduction/" rel="noopener noreferrer"&gt;vector tilesets&lt;/a&gt; contain the actual data shown on the map, served over the network as small chunks of unstyled data representing a quadrangle of the earth's surface. &lt;a href="https://docs.mapbox.com/data/tilesets/reference/mapbox-streets-v8/" rel="noopener noreferrer"&gt;&lt;code&gt;mapbox-streets-v8&lt;/code&gt;&lt;/a&gt; does the heavy lifting and contains data for most of what you'll see on any Mapbox style (streets, place names, parks/land use, buildings, etc). The image below contains a simplified rendering of the rich data included in the streets-v8 tileset, including streets, place labels, points of interest, building footprints, etc.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6v9x09hbzt82awkehrua.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6v9x09hbzt82awkehrua.png" alt=" " width="800" height="523"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All this complexity gets you to a pretty great starting point, a well-designed "basemap" of the whole planet, at every zoom level from the full globe down to streets and intersections. Many developers never need to edit layers in the basemap, they take it as-is and concern themselves only with styling their own data they add to the map. Others may want complete control, changing colors to match a brand, removing or modifying whole sets of map labels, or changing the width of streets.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Style Specification
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://docs.mapbox.com/style-spec/" rel="noopener noreferrer"&gt;Mapbox Style Specification&lt;/a&gt; is the formal definition of what a style is, and how it should be structured. It lives at &lt;a href="https://docs.mapbox.com/style-spec/" rel="noopener noreferrer"&gt;docs.mapbox.com/style-spec&lt;/a&gt; and defines the required properties, valid values for each property, and the overall structure of the style JSON. If you want to understand how styles work under the hood, or if you want to create your own custom styles from scratch, the style spec is the place to start. It is also a great reference for understanding the different properties and options available when working with styles, and can help you troubleshoot issues when your style isn't working as expected. &lt;/p&gt;

&lt;p&gt;Every possible style property is outlined in the &lt;a href="https://docs.mapbox.com/style-spec" rel="noopener noreferrer"&gt;style spec&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjcl16cz9wc2irokougdp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjcl16cz9wc2irokougdp.png" alt=" " width="799" height="528"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you are maintaining complex styles on your own, there are &lt;a href="https://github.com/mapbox/mapbox-gl-js/blob/main/src/style-spec/README.md" rel="noopener noreferrer"&gt;various utilities&lt;/a&gt; available to help you validate your style JSON against the specification, and to catch errors before they cause issues in your apps. &lt;/p&gt;

&lt;h2&gt;
  
  
  Other ways to get a style JSON into your map
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;mapbox://&lt;/code&gt; URLs are the most common way to get a style into your map, but they are not the only way.&lt;/p&gt;

&lt;p&gt;As mentioned earlier, the style is just JSON, and that JSON can come from anywhere. For example, if I were to strip down the complex style JSON above, and include only a single layer for buildings, the style might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"layers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"building"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fill"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"streets-v8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"source-layer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"building"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"minzoom"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"filter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="s2"&gt;"all"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"!="&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"get"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"building:part"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"=="&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"get"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"underground"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"false"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"layout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"paint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"fill-color"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#ccc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"sources"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"streets-v8"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mapbox://mapbox.mapbox-streets-v8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vector"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have also removed optional properties: &lt;code&gt;center&lt;/code&gt;, &lt;code&gt;zoom&lt;/code&gt;, &lt;code&gt;fog&lt;/code&gt;, &lt;code&gt;projection&lt;/code&gt;, &lt;code&gt;visibility&lt;/code&gt;, &lt;code&gt;created&lt;/code&gt;, &lt;code&gt;modified&lt;/code&gt;, &lt;code&gt;owner&lt;/code&gt;, &lt;code&gt;id&lt;/code&gt;, and &lt;code&gt;draft&lt;/code&gt;. Bear in mind that the above is the &lt;em&gt;bare minimum&lt;/em&gt; that the renderer needs to display something from the streets-v8 tileset. So how do we get this style into the map?&lt;/p&gt;

&lt;h3&gt;
  
  
  Load JSON from your codebase
&lt;/h3&gt;

&lt;p&gt;You can load style JSON from a file in your codebase, or as a JSON string defined in your source code. This codepen shows the minimal style JSON above, implemented as a JSON object in the JavaScript code just before map instantiation:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/chris-whong/embed/myObmBw?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h4&gt;
  
  
  Web
&lt;/h4&gt;

&lt;p&gt;In a web project, this style could be saved as &lt;code&gt;style.json&lt;/code&gt; and imported as a JSON object using a bundler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../data/style.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; 

&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;mapboxgl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;zoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;15.5&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It can also be defined as an object in the JavaScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;version&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;layers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sources&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;mapboxgl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;zoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;15.5&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Mobile Applications (iOS, Android, Flutter)
&lt;/h4&gt;

&lt;p&gt;For mobile applications, there's no equivalent to loading the style JSON from an object stored in a variable, so you'll be working with the JSON as a string. You can load the JSON from an asset file in your codebase, or define it as a string directly in your code.&lt;/p&gt;

&lt;p&gt;For example, in iOS you could load the JSON from a file in your app bundle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;styleURL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Bundle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;forResource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"style"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;withExtension&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;MapView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;styleURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;styleURL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addSubview&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or in Flutter you could define the string directly in your code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;style&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'''{
  "version": 8,
  "layers": [ ... ],
  "sources": { ... }         
}'''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MapboxMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;styleString:&lt;/span&gt; &lt;span class="n"&gt;style&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;initialCameraPosition:&lt;/span&gt; &lt;span class="n"&gt;CameraPosition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nl"&gt;target:&lt;/span&gt; &lt;span class="n"&gt;LatLng&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nl"&gt;zoom:&lt;/span&gt; &lt;span class="mf"&gt;15.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Host a JSON file somewhere (anywhere!)
&lt;/h3&gt;

&lt;p&gt;You can add any URL to the &lt;code&gt;style&lt;/code&gt; option in Mapbox GL JS. This means you can host your style JSON on your own server, an S3 bucket, github raw link, or any other web-accessible location. The only requirement is that the style be accessible at the time of map instantiation, and that it be valid style JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;mapboxgl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://example.com/my-style.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;zoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;15.5&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On mobile, loading a style from a URL works the same way, just make sure the URL is accessible from the device:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;styleURL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;string&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"https://example.com/my-style.json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;MapView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;styleURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;styleURL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addSubview&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  When would I ever need to do this?
&lt;/h3&gt;

&lt;p&gt;Loading a style JSON from your codebase can be useful in development, where you want to move quickly and just get something on the screen without worrying about hosting and network requests. It also allows you to keep your style JSON in version control alongside your app's code, which can be helpful for tracking changes and collaborating with other developers.&lt;/p&gt;

&lt;p&gt;There are also plenty of use cases where a full-on basemap isn't needed, such as indoor maps, transit maps, event seating charts, or any map where the data is the star of the show and a real-world basemap would just be visual noise. In these cases, you can maintain a simple style JSON with just your custom sources and layers, and load it directly into the map without having to worry about the complexity of a full basemap style.&lt;/p&gt;

&lt;p&gt;For a full style with hundreds of layers and thousands of lines of JSON, it's often less practical to maintain it in your codebase, but some teams may prefer this so they can keep styles in the same codebase as their app, be able to explore changes in version control, and have full control over how and when styles are updated and deployed. &lt;/p&gt;

&lt;p&gt;However, modern changes to the style spec allow for importing one style into another, which makes maintaining your own top-level style JSON much more manageable. See the &lt;strong&gt;Enter the Standard Style (and style "imports")&lt;/strong&gt; section below.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation patterns for using Mapbox-hosted Classic Styles
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1) Load a Mapbox classic style (mapbox://styles/mapbox/streets-v12) and add your data at runtime
&lt;/h3&gt;

&lt;p&gt;This approach treats the initial map load and its style as the basemap and adds additional sources and layers after the map has loaded. This approach lends itself well to using GeoJSON sources and layers when you have small datasets that you want to add to a style.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;mapboxgl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mapbox://styles/mapbox/streets-v12&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;zoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;15.5&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;load&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;geojson&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://example.com/my-data.geojson&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addLayer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-data-layer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;circle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;paint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;circle-radius&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;circle-color&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#f00&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2) Make a custom style based on (e.g. cloned from) a Mapbox-hosted style
&lt;/h3&gt;

&lt;p&gt;With this approach, you start with a clone of a Mapbox style and add your data in the Mapbox Style Editor. Once your style is published, you can use it in your frontend maps using its &lt;code&gt;mapbox://&lt;/code&gt; URL. Behind the scenes there is still a style JSON with thousands of lines, but you the developer never have to interact with it directly, and can make all changes through the UI of the Style Editor.&lt;/p&gt;

&lt;p&gt;This approach is more common for developers who have custom tilesets hosted on Mapbox, either created using Mapbox Tiling Service or via the Data Workbench. Adding a custom tileset as a &lt;code&gt;vector&lt;/code&gt; source at runtime is still possible, but it's more straightforward to add the tileset to a custom style and use the Style Editor to get it looking the way you want. &lt;/p&gt;

&lt;p&gt;This image from the Style Editor shows the &lt;strong&gt;Dark&lt;/strong&gt; classic style, with full control over each of its 100+ layers. Developers get full control, but manual updates are required if the underlying data changes or new features are released.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fql2h28kxpzadv0p10evs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fql2h28kxpzadv0p10evs.png" alt=" " width="800" height="514"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This approach is also ideal if you want the same map on multiple platforms, as you can maintain one style in the Style Editor and load it on web, iOS, Android, and/or Flutter with the same &lt;code&gt;mapbox://&lt;/code&gt; URL. No additional code is necessary to add your custom sources and layers, as they are already baked into the custom style.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The Style Editor used to allow for the creation of new styles based on the Classic Styles (Streets, Light, Dark, etc.), but this option has been removed. You can still use this approach by adding any of the styles in the &lt;a href="https://www.mapbox.com/gallery" rel="noopener noreferrer"&gt;Mapbox Gallery's&lt;/a&gt; &lt;strong&gt;Community Templates&lt;/strong&gt; tab. Copying a style from the gallery clones it, and your new style is a carbon copy of the original template. Any changes you make are yours to manage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;mapboxgl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mapbox://styles/your-username/your-style-id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;zoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;15.5&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The "Maintenance Tax" of Classic Styles
&lt;/h3&gt;

&lt;p&gt;The classic style paradigm has been around since the earliest days of GL-based mapping at Mapbox, and it remains a powerful and flexible approach — but that flexibility comes with a cost: &lt;strong&gt;you own the style JSON, which means you own the maintenance burden.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you fork a classic style, you're taking a snapshot frozen in time. Mapbox continuously improves both the renderer and the style spec together — new features like HD Roads or Indoor Maps aren't just style layer changes, they're coordinated updates across the rendering engine, the style specification, and the style JSON simultaneously. Classic style users who want these features have to manually integrate those style-side changes without breaking their own customizations. &lt;/p&gt;

&lt;p&gt;The more custom styles you maintain, and the more heavily you've modified them, the steeper this tax becomes. What's the solution? Read on to learn about how the &lt;strong&gt;Mapbox Standard Style&lt;/strong&gt; changes the whole style paradigm. &lt;/p&gt;

&lt;h2&gt;
  
  
  Enter the Mapbox Standard Style (and style "imports")
&lt;/h2&gt;

&lt;p&gt;I've spent all this time telling you about the old way of doing things and the underlying structure of a style so I can better explain the value of &lt;em&gt;the new way&lt;/em&gt;. The &lt;a href="https://docs.mapbox.com/map-styles/standard/guides/" rel="noopener noreferrer"&gt;Mapbox Standard Style&lt;/a&gt; is our premier, multipurpose basemap. At its core it is a style just like any other, with sources, layers, sprites, glyphs, etc, but it is designed to be &lt;em&gt;imported&lt;/em&gt; and &lt;em&gt;configured&lt;/em&gt; instead of being &lt;em&gt;cloned&lt;/em&gt; and &lt;em&gt;modified&lt;/em&gt;. This is a (relatively) new pattern for Mapbox styles, abstracting away the complexity of the basemap and allowing developers to focus their attention on the data they are presenting &lt;em&gt;on top of the basemap&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;In fact, when you load a map with the Mapbox Standard Style (either by not specifying a style at all, or by using the &lt;code&gt;mapbox://styles/mapbox/standard&lt;/code&gt; URL), the renderer actually creates a new style on the fly that &lt;em&gt;imports&lt;/em&gt; the Standard Style. The style that's actually loaded looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sources"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"layers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"imports"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"basemap"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mapbox://styles/mapbox/standard"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mapbox Standard also introduces the concept of &lt;a href="https://docs.mapbox.com/style-spec/reference/slots/" rel="noopener noreferrer"&gt;slots&lt;/a&gt;, which are placeholders in the layer stack of the imported style where any custom layers from the importing style can be inserted. For example, you may want your custom polygon layer to appear above paths and roads, but below buildings, models and labels, so you could place it in the &lt;code&gt;middle&lt;/code&gt; slot. For more on slots in Mapbox Standard, see the &lt;a href="http://docs.mapbox.com/map-styles/standard/guides/#slots" rel="noopener noreferrer"&gt;Mapbox Standard Style documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But what if you want to modify the individual layers in the Standard Style? &lt;em&gt;Direct manipulation of these layers is not possible.&lt;/em&gt; This is part of the tradeoff of style imports. Instead of direct access to the layers, the style has been designed with a set of &lt;a href="https://docs.mapbox.com/map-styles/standard/api/#configuration-properties" rel="noopener noreferrer"&gt;configurable properties&lt;/a&gt;. Think of it as an API for the style. You can summon standard with or without map labels, set the color of water, land, and park areas, and toggle 3D buildings.&lt;/p&gt;

&lt;p&gt;Here's what that looks like in a Style JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sources"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"layers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"imports"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"basemap"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mapbox://styles/mapbox/standard"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"config"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;showPlaceLabels:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;show&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="err"&gt;dObjects:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;colorWater:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'#&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="err"&gt;F'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Best of all, the Standard Style is in continuous development, and because you are importing it instead of copying it, you get all the updates and improvements as they are released, without having to do any work to maintain it. &lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation Patterns for the Mapbox Standard Style
&lt;/h2&gt;

&lt;p&gt;Here I'll outline three common patterns for implementing the Mapbox Standard Style in your maps, and the pros and cons of each.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Instantiate with standard + config parameters, add data as runtime layers and/or Markers/Annotations
&lt;/h3&gt;

&lt;p&gt;Mapbox Standard is now the default style for all Mapbox renderers, so specifying a &lt;code&gt;style&lt;/code&gt; when you instantiate a map is no longer required. The renderers also accept &lt;code&gt;config&lt;/code&gt; properties during map instantiation, so you can configure the Standard Style without having to create style JSON.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;mapboxgl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;zoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;15.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// configure the Mapbox Standard Style before the map loads&lt;/span&gt;
    &lt;span class="na"&gt;basemap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;showPlaceLabels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;show3dObjects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;colorWater&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#00F&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;load&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;geojson&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://example.com/my-data.geojson&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addLayer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-data-layer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;circle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;paint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;circle-radius&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;circle-color&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#f00&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simple and straightforward, with no need to maintain any style JSON&lt;/li&gt;
&lt;li&gt;Always up to date with the latest version of the Standard Style&lt;/li&gt;
&lt;li&gt;Configurable without needing to understand the complexity of the style JSON&lt;/li&gt;
&lt;li&gt;The basemap "just works" and you focus on adding your data after the map loads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Duplicate client-side configuration if you are using the same style on multiple platforms (web, iOS, Android, Flutter)&lt;/li&gt;
&lt;li&gt;Verbose if you are adding a lot of runtime layers and sources, as all of that has to be done in code instead of in a style JSON.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Create a custom style JSON that imports the Standard Style, and load the map with the custom style's URL
&lt;/h3&gt;

&lt;p&gt;This approach means crafting your own style JSON, but it's a more manageable size since the Standard Style is abstracted away via an &lt;code&gt;import&lt;/code&gt;. You can add your custom sources and layers to the style JSON, and configure the Standard Style via the &lt;code&gt;config&lt;/code&gt; property in the import. This style JSON can be hosted anywhere, or included directly in your app's codebase.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;version&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sources&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my-data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;geojson&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://example.com/my-data.geojson&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;layers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my-data-layer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;circle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;source&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my-data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;paint&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;circle-radius&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;circle-color&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#f00&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;imports&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;basemap&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;url&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mapbox://styles/mapbox/standard&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;config&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;showPlaceLabels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;show3dObjects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;colorWater&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#00F&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;mapboxgl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;zoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;15.5&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Centralized style management in a single JSON file, with no need for client-side configuration&lt;/li&gt;
&lt;li&gt;Still up to date with the latest version of the Standard Style, without having to manage it yourself&lt;/li&gt;
&lt;li&gt;Easier to maintain and update custom sources and layers in a style JSON than in code&lt;/li&gt;
&lt;li&gt;Compatible with GeoJSON, image, and video source types&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Requires understanding of the style JSON structure and the concept of imports&lt;/li&gt;
&lt;li&gt;Still requires maintaining a custom style JSON, which may be more work than just adding runtime layers&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Create a custom style in the Style Editor
&lt;/h3&gt;

&lt;p&gt;All custom styles created via the &lt;a href="https://studio.mapbox.com/" rel="noopener noreferrer"&gt;Mapbox Style Editor&lt;/a&gt; now include the Standard Style as an import, so if you are using the Style Editor to create your styles, you are already using the Standard Style under the hood, and can take advantage of all its features and updates without having to do anything extra. You can upload your data to the &lt;a href="https://console.mapbox.com/studio/data-workbench" rel="noopener noreferrer"&gt;data workbench&lt;/a&gt;, add it to a style, configure the basemap, and get a mapbox:// URL that you can use across all platforms, with the confidence that your basemap is always up to date and looking great.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;mapboxgl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mapbox://styles/your-username/your-style-id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;zoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;15.5&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No need to maintain any style JSON, as everything is done through the Mapbox Style Editor and served via a mapbox:// URL&lt;/li&gt;
&lt;li&gt;The most user-friendly way to create and manage styles, with a visual editor and real-time preview&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;May not be ideal for teams that prefer to manage styles in code or version control, as styles in the Style Editor are not as easily versioned or diffed as JSON files&lt;/li&gt;
&lt;li&gt;Only compatible with vector and raster source types from Mapbox-hosted tilesets. If you want to use GeoJSON, image, or video sources, you would need to add those as runtime sources and layers in your app's code, which may be less than ideal if you have a lot of custom data to add.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Comparing The Standard Style with Classic Styles
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Style&lt;/th&gt;
&lt;th&gt;Usage approaches&lt;/th&gt;
&lt;th&gt;Customization&lt;/th&gt;
&lt;th&gt;Updates&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mapbox Standard Style&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;- Load by style URL; add your data layers at runtime&lt;br&gt;- Create a custom style JSON that &lt;code&gt;imports&lt;/code&gt; Standard as a dependency&lt;br&gt;- Build a custom style in the Mapbox Style Editor that imports Standard&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;- Predefined configuration parameters (no raw layer editing)&lt;br&gt;- Theme via lookup table (LUT) for cohesive color grading&lt;br&gt;- Specify key colors to globally tint the style&lt;br&gt;- Toggle POIs, landmarks, transit on/off&lt;br&gt;- Light presets (day, dusk, dawn, night)&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;&lt;strong&gt;Automatic&lt;/strong&gt; — Referenced by URL or import, Mapbox continuously updates the style and you receive all improvements (new features, data, rendering quality) automatically, without any action on your part.&lt;/small&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mapbox Classic Styles&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;- Load a Mapbox-hosted style URL (Streets, Satellite, Outdoors, etc.); add your data layers at runtime&lt;br&gt;- Fork a Mapbox classic style into your own style JSON and customize freely&lt;br&gt;- Create a custom style in the Mapbox Style Editor based on a classic style template, add your own data sources and layers&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;- Full access to every layer — edit colors, fonts, filters, zoom ranges directly&lt;br&gt;- Add, remove, or reorder any layer&lt;br&gt;- Customize data sources, expressions, and layout properties without restriction&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;&lt;strong&gt;Manual&lt;/strong&gt; — When you fork or copy a classic style, it becomes a snapshot. You own the style JSON and must manually apply any upstream changes from Mapbox; there are no automatic updates.&lt;/small&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Standard Style replaces multiple Classic Styles
&lt;/h3&gt;

&lt;p&gt;The Mapbox Standard Style also includes config parameters for color themes and light presets, and combing these can yield drastically different looks. &lt;/p&gt;

&lt;p&gt;With Classic Styles, we had whole different styles for different looks, like &lt;code&gt;streets&lt;/code&gt;, &lt;code&gt;light&lt;/code&gt;, and &lt;code&gt;dark&lt;/code&gt;. Streets was a good all-purpose style for a basemap, with a wide variety of colors and details. Light and dark were designed to be more subdued, ideal for maps where the overlaid data should be the star of the show, and the basemap should recede into the background. With the Standard Style, you can achieve all three looks with one style, and have more control over the colors and details than ever before.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Classic Style&lt;/th&gt;
&lt;th&gt;Equivalent using Standard Style&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Mapbox Streets&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;lightPreset: 'day'&lt;/code&gt; (default)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mapbox Light&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;lightPreset: 'day'&lt;/code&gt; + &lt;code&gt;colorTheme: 'monochrome'&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mapbox Dark&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;lightPreset: 'night'&lt;/code&gt; + &lt;code&gt;colorTheme: 'monochrome'&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;There's no quick configuration to replicate the &lt;strong&gt;Outdoors&lt;/strong&gt; classic style with Mapbox Standard, but there is a style template which imports Standard and adds 3D terrain, hillshading, and topographic lines to get you the same effect. See the &lt;a href="https://www.mapbox.com/gallery#mapbox-outdoors" rel="noopener noreferrer"&gt;Outdoors template&lt;/a&gt; in the Mapbox Style Gallery. You can clone this template in the Style Editor and add your own data to it. Keep in mind that you'll get continuous updates to the Standard Style since it's imported, but the additional layers are yours to manage. &lt;/p&gt;

&lt;h2&gt;
  
  
  Modifying styles at runtime
&lt;/h2&gt;

&lt;p&gt;Layers in a map style can be modified at runtime, and involves passing in bits of layer JSON to the &lt;code&gt;setLayoutProperty&lt;/code&gt; and &lt;code&gt;setPaintProperty&lt;/code&gt; methods on the map. This is a common pattern for adding interactivity to a map, such as changing the color of a layer when it's clicked, or toggling the visibility of a layer with a button click.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setLayoutProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-layer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;visibility&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setPaintProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-layer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;circle-color&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#0f0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since imported styles don't allow direct control of their internal map layers, control happens using the imported style's config parameters. All Mapbox renderers now include a method for modifying the config parameters of an imported style at runtime. This means you can change the colors, labels, and 3D settings of the Standard Style on the fly, without having to reload the map or create a new style JSON. This could be used to make the light preset (dawn, day, dusk, night) dynamic based on the user's actual time of day, or to toggle place labels on and off with a button click.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setConfigProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;basemap&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lightPreset&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dusk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setConfigProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;basemap&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;showPlaceLabels&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See the &lt;a href="https://docs.mapbox.com/mapbox-gl-js/example/set-config-property/" rel="noopener noreferrer"&gt;setConfigProperty example&lt;/a&gt; for more details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building your own imported styles
&lt;/h2&gt;

&lt;p&gt;While not widely documented, the style import pattern is not limited to just the Mapbox Standard Style. You can create your own styles that are designed to be imported into other styles, and have their own configuration options specific to your needs. This is a powerful pattern for managing complex styles, as it allows you to break down a large style into smaller, more manageable pieces, and reuse common elements across multiple styles.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sources"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"layers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"imports"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"basemap"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mapbox://styles/mapbox/standard"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"config"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"transit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mapbox://styles/your-username/transit-style"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"config"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; 
        &lt;/span&gt;&lt;span class="err"&gt;showAcessibleTransit:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;showTransitLabels:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Other style tips and tricks
&lt;/h2&gt;

&lt;p&gt;Here are a few more tips.&lt;/p&gt;

&lt;h3&gt;
  
  
  Define GeoJSON source data in the style
&lt;/h3&gt;

&lt;p&gt;You can store data for &lt;a href="https://docs.mapbox.com/mapbox-gl-js/style-spec/sources/#geojson" rel="noopener noreferrer"&gt;GeoJSON sources&lt;/a&gt; right in the style JSON, instead of linking to it. This is ideal for small datasets that are tightly coupled to the style and don't need to be updated independently. Just add a &lt;code&gt;data&lt;/code&gt; property to your source definition and include the GeoJSON directly in the style JSON.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sources"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"my-data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"geojson"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FeatureCollection"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"features"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Feature"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"geometry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Point"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"coordinates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;-72&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"My Point"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Manipulate a style via code
&lt;/h3&gt;

&lt;p&gt;When working with a style as a JavaScript object, you can provide GeoJSON data as a variable or assemble any part of the object programmatically before using it in a map.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;FeatureCollection&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;features&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Feature&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;geometry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Point&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;coordinates&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;My Point&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my-data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;geojson&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;myData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Add a source to the style&lt;/span&gt;
&lt;span class="nx"&gt;myStyle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vector&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://someserver.com/path-to-tilejson.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Define the default camera position in the style
&lt;/h3&gt;

&lt;p&gt;Mapbox renderers honor camera properties (&lt;code&gt;center&lt;/code&gt;, &lt;code&gt;zoom&lt;/code&gt;, &lt;code&gt;pitch&lt;/code&gt;, &lt;code&gt;bearing&lt;/code&gt;) from the style JSON, so you can set the initial map view in the style instead of in code. This is useful for ensuring a consistent initial view across platforms, or when maintaining a style separately from the app code. These properties exist at the &lt;a href="https://docs.mapbox.com/style-spec/reference/root/" rel="noopener noreferrer"&gt;root&lt;/a&gt; of the style object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"center"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;58.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sources"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"layers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;When it comes to styles, you've got options. Whether you just want a quick and easy way to get a map on the screen, or you want complete control over every layer and source, the style specification and Mapbox's tools are flexible and can accommodate your needs. &lt;/p&gt;

&lt;p&gt;The Mapbox Standard Style and the style import pattern have made it easier than ever to get a great-looking basemap up and running, while still allowing for customization and control when you need it.&lt;/p&gt;

&lt;p&gt;Hopefully this post has demystified some of the magic and recent changes around map styles, and given you some ideas for how to work with them in your own projects. If you have any questions or want to share your own style tips and tricks, feel free to reach out or leave a comment!&lt;/p&gt;

</description>
      <category>mapbox</category>
      <category>webmaps</category>
      <category>mobilemaps</category>
      <category>mapdesign</category>
    </item>
    <item>
      <title>Mapbox GL JS adds support for PMTiles vector sources</title>
      <dc:creator>Chris Whong</dc:creator>
      <pubDate>Thu, 30 Apr 2026 16:15:47 +0000</pubDate>
      <link>https://dev.to/mapbox/mapbox-gl-js-adds-support-for-pmtiles-vector-and-raster-sources-141b</link>
      <guid>https://dev.to/mapbox/mapbox-gl-js-adds-support-for-pmtiles-vector-and-raster-sources-141b</guid>
      <description>&lt;p&gt;Mapbox GL JS &lt;code&gt;v3.21.0&lt;/code&gt; added support for &lt;code&gt;vector&lt;/code&gt; sources using the &lt;a href="https://docs.protomaps.com/pmtiles/" rel="noopener noreferrer"&gt;PMTiles&lt;/a&gt; protocol — another option in your toolkit for bringing spatial data into Mapbox-powered web maps.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is PMTiles?
&lt;/h2&gt;

&lt;p&gt;PMTiles is a single-file &lt;a href="https://github.com/protomaps/PMTiles/blob/main/spec/v3/spec.md" rel="noopener noreferrer"&gt;open source&lt;/a&gt; format for storing tiled map data — vector or raster — built for random access over HTTP range requests. Host one on any static file server and you have a fully functional tileset, no complicated backend required.&lt;/p&gt;

&lt;p&gt;Traditional tile serving follows a familiar pattern: each tile gets its own URL using an x/y/z scheme, and behind that URL is either a directory of pre-cut files or a service dynamically pulling tiles from a database. Either way, there's infrastructure involved.&lt;/p&gt;

&lt;p&gt;PMTiles sidesteps all of that. Every tile request hits the same URL — what changes is the byte range. A tile's x/y/z coordinates are converted into a specific byte offset within the file, and the server returns just that chunk. The client receives it and renders it like any other tile, none the wiser.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzcbervynq3keujssynm8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzcbervynq3keujssynm8.png" alt=" " width="800" height="567"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Using PMTiles in Mapbox GL JS
&lt;/h2&gt;

&lt;p&gt;You're probably already familiar with adding a vector tileset to your map at runtime as a &lt;code&gt;vector&lt;/code&gt; source. The existing options are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A URL to a &lt;code&gt;TileJSON&lt;/code&gt;(&lt;a href="https://docs.mapbox.com/help/glossary/tilejson/" rel="noopener noreferrer"&gt;https://docs.mapbox.com/help/glossary/tilejson/&lt;/a&gt;) metadata file containing the tileset's URL template&lt;/li&gt;
&lt;li&gt;A Mapbox-hosted tileset via its &lt;code&gt;mapbox://&lt;/code&gt; URL (a shorthand for a TileJSON hosted by Mapbox)&lt;/li&gt;
&lt;li&gt;An explicit URL template with &lt;code&gt;{z}/{x}/{y}&lt;/code&gt; placeholders
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// TileJSON URL&lt;/span&gt;
&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mapbox-terrain&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vector&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://somedomain.com/tileset.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Mapbox-hosted tileset&lt;/span&gt;
&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mapbox-terrain&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vector&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mapbox://mapbox.mapbox-terrain-v2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Explicit x/y/z URL template&lt;/span&gt;
&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;some-vector-tileset&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vector&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;tiles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://somedomain.com/tileset/{z}/{x}/{y}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In all three cases, Mapbox GL JS ends up with a &lt;code&gt;{z}/{x}/{y}&lt;/code&gt; template URL and uses it to request tiles based on what's in view.&lt;/p&gt;

&lt;p&gt;With PMTiles support, you can now pass a &lt;code&gt;.pmtiles&lt;/code&gt; file URL directly as the &lt;code&gt;url&lt;/code&gt; property:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mapbox-terrain&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vector&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://somedomain.com/tileset.pmtiles&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. Mapbox GL JS detects the &lt;code&gt;.pmtiles&lt;/code&gt; extension and handles range requests behind the scenes.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Working Example
&lt;/h2&gt;

&lt;p&gt;The example below shows &lt;a href="https://earthquake.usgs.gov/earthquakes/feed/v1.0/geojson.php" rel="noopener noreferrer"&gt;USGS Earthquake data&lt;/a&gt; served from a PMTiles file and visualized with a &lt;a href="https://docs.mapbox.com/style-spec/reference/layers/#circle" rel="noopener noreferrer"&gt;&lt;code&gt;circle&lt;/code&gt;&lt;/a&gt;layer. &lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/chris-whong/embed/PwGLJvN?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.mapbox.com/mapbox-gl-js/example/pmtiles-vector-source/" rel="noopener noreferrer"&gt;See the full example on docs.mapbox.com →&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's the JavaScript where &lt;code&gt;addSource()&lt;/code&gt; is called with a URL to &lt;code&gt;.pmtiles&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;mapboxgl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accessToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR_MAPBOX_ACCESS_TOKEN&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;mapboxgl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;container&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;map&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mapbox://styles/mapbox/standard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;basemap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;monochrome&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;lightPreset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;night&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;zoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.78&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;103.64548&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;51.14245&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;load&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pmtiles-earthquakes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vector&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://docs.mapbox.com/mapbox-gl-js/assets/earthquakes.pmtiles&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addLayer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pmtiles-vector-layer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;circle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pmtiles-earthquakes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;source-layer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;earthquakes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;paint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;circle-color&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;transparent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;circle-stroke-color&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;teal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;circle-stroke-width&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;circle-radius&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;circle-emissive-strength&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How do I make a PMTiles?
&lt;/h2&gt;

&lt;p&gt;The PMTiles file in the example above was generated with &lt;a href="https://github.com/felt/tippecanoe" rel="noopener noreferrer"&gt;&lt;code&gt;tippecanoe&lt;/code&gt;&lt;/a&gt; from a week's worth of USGS earthquake data (~2,400 point features) and hosted on the same S3 bucket as docs.mapbox.com:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tippecanoe &lt;span class="nt"&gt;-o&lt;/span&gt; public/earthquakes.pmtiles &lt;span class="nt"&gt;-Z0&lt;/span&gt; &lt;span class="nt"&gt;-z6&lt;/span&gt; &lt;span class="nt"&gt;-r1&lt;/span&gt; &lt;span class="nt"&gt;-pk&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; earthquakes data/earthquakes-week.geojson &lt;span class="nt"&gt;--force&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what each flag does:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Flag&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-o public/earthquakes.pmtiles&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Output file path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-Z0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Minimum zoom level (global view)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-z6&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Maximum zoom level&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-r1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Drop rate of 1 — don't drop any features at any zoom&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-pk&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Allow tiles to exceed the 500KB size limit to preserve all features&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-l earthquakes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Layer name, referenced as &lt;code&gt;source-layer&lt;/code&gt; in your style&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;data/earthquakes-week.geojson&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Input GeoJSON&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--force&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Overwrite output file without prompting&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Try It Yourself
&lt;/h2&gt;

&lt;p&gt;Looking for publicly hosted PMTiles files to experiment with? Check out &lt;a href="https://pmtiles.io/" rel="noopener noreferrer"&gt;pmtiles.io&lt;/a&gt; — it includes several global vector and raster tilesets and is a handy tool for previewing PMTiles files you generate yourself.&lt;/p&gt;

&lt;p&gt;We'd love to hear about what you're building — drop into the &lt;code&gt;#web&lt;/code&gt; channel in the &lt;a href="https://discord.com/invite/uMpcC5RmJh" rel="noopener noreferrer"&gt;Mapbox Discord&lt;/a&gt; or share a post on &lt;a href="https://www.reddit.com/r/mapbox/" rel="noopener noreferrer"&gt;r/mapbox&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>spatialdata</category>
      <category>pmtiles</category>
      <category>web</category>
      <category>webmapping</category>
    </item>
    <item>
      <title>A GUI editor for Mapbox Color Themes</title>
      <dc:creator>Chris Whong</dc:creator>
      <pubDate>Tue, 03 Mar 2026 17:38:03 +0000</pubDate>
      <link>https://dev.to/mapbox/a-gui-editor-for-mapbox-color-themes-5fho</link>
      <guid>https://dev.to/mapbox/a-gui-editor-for-mapbox-color-themes-5fho</guid>
      <description>&lt;p&gt;&lt;em&gt;Introducing a browser-based prototype tool for iterating on custom Mapbox Standard Style themes in real time — no terminal required.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;tl;dr: &lt;strong&gt;&lt;a href="https://chriswhong.github.io/mapboxgl-theme-editor/" rel="noopener noreferrer"&gt;Open the Mapbox Standard Style Theme Editor →&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;Maps are a visual medium, and color is one of the most powerful tools in a cartographer's toolkit. Whether you're building a dark-mode experience for a nighttime delivery app, a muted editorial look for a data visualization, or a vivid branded style for a consumer product, the color of your basemap sets the entire tone.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.mapbox.com/map-styles/standard/guides/" rel="noopener noreferrer"&gt;Mapbox Standard&lt;/a&gt; — Mapbox's premier basemap style — supports deep color customization through a mechanism called &lt;strong&gt;Lookup Tables (LUTs)&lt;/strong&gt;. Most developers haven't encountered LUTs before. They're more at home in film post-production than web development. But once you understand how they work, they're an elegant and powerful way to retheme a map.&lt;/p&gt;

&lt;p&gt;This post explains what LUTs are, how Mapbox Standard uses them, and introduces a prototype web tool I built for experimenting with custom color themes in real time: the &lt;a href="https://chriswhong.github.io/mapboxgl-theme-editor/" rel="noopener noreferrer"&gt;Mapbox Standard Style Theme Editor&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Mapbox Standard Style
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.mapbox.com/map-styles/standard/guides/" rel="noopener noreferrer"&gt;Mapbox Standard&lt;/a&gt; is Mapbox's flagship basemap. It features 3D buildings, photorealistic landmarks, dynamic lighting, and a set of built-in configuration options that make it easy to adapt for a wide range of use cases.&lt;/p&gt;

&lt;p&gt;For many applications, the quickest path to a custom look is the &lt;code&gt;lightPreset&lt;/code&gt; option, which controls ambient and directional lighting to simulate different times of day — dawn, day, dusk, or night. Combined with predefined &lt;code&gt;theme&lt;/code&gt; options like &lt;code&gt;faded&lt;/code&gt;, and &lt;code&gt;monochrome&lt;/code&gt;, you can achieve a lot without writing much code.&lt;/p&gt;

&lt;p&gt;Below you can see Mapbox Standard with its default color theme and daytime light as compared to the &lt;code&gt;night&lt;/code&gt; lighting preset and &lt;code&gt;monochrome&lt;/code&gt; theme, creating a subdued dark basemap:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhmbra9qb2iczrbedunn9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhmbra9qb2iczrbedunn9.png" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgi7bljau1job3ri31bfq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgi7bljau1job3ri31bfq.png" alt=" " width="800" height="798"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also control some colors in Mapbox Standard via the configuration options, such as &lt;code&gt;colorWater&lt;/code&gt;, &lt;code&gt;colorLand&lt;/code&gt;, &lt;code&gt;colorGreenSpace&lt;/code&gt;, etc, so a Lookup Table is not required to start customizing colors on the map.&lt;/p&gt;

&lt;p&gt;You can try out the preset themes and individual color config options in the &lt;a href="https://docs.mapbox.com/playground/standard-style/" rel="noopener noreferrer"&gt;Mapbox Standard Style Playground&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But for those who want event more control over the color palette, Mapbox Standard supports setting &lt;code&gt;theme&lt;/code&gt; to &lt;code&gt;custom&lt;/code&gt; and supplying your own &lt;strong&gt;Lookup Table&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Exactly Is a LUT?
&lt;/h2&gt;

&lt;p&gt;A Lookup Table — or LUT — is a data structure that maps input color values to output color values. In image and video processing, a LUT is typically encoded as a small image (sometimes called a "HALD CLUT" or "color cube") where each pixel's position in the image encodes an input RGB value, and its color encodes the corresponding output.&lt;/p&gt;

&lt;p&gt;Think of it as a complete color transformation recipe. Every possible input color has a predetermined output, and the LUT defines that mapping across the entire visible spectrum at once. Apply the LUT to any image, and every pixel is remapped accordingly.&lt;/p&gt;

&lt;p&gt;This image shows the "identity LUT", with no colors modified. It's a png with a repeating set of "squares" laid out horizontally. Each square shows red values along the x-axis, and green values along the y-axis. If you can mentally stack the squares, they become a cube, with blue values along the z-axis. The cube represents all possible color values and is used to map any input color to a corresponding output color.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4091tqu8a1v8q4r14psk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4091tqu8a1v8q4r14psk.png" width="256" height="16"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In video production, LUTs create consistent "looks" — film emulation, color grading, log-to-linear conversions. In photography, they power the one-click preset packs ubiquitous in Lightroom marketplaces. In Mapbox Standard, they're the engine behind custom color themes.&lt;/p&gt;

&lt;p&gt;The key insight is that the LUT doesn't know or care what it's being applied to. It just says: "If you see this color, output that color." Mapbox Standard renders the map to an intermediate image, applies your LUT, and out comes a fully rethemed map — buildings, roads, labels, land, water — all transformed consistently.&lt;/p&gt;

&lt;p&gt;Here is a custom LUT that applies a 15-degree hue rotation and increased saturation. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6juw9pyu8pkvwxtf3up2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6juw9pyu8pkvwxtf3up2.png" alt=" " width="256" height="16"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At first glance it looks the same as the identity LUT above, but look closely and you'll see subtle differences. You can see it applied to both a standalone image (from &lt;a href="https://commons.wikimedia.org/wiki/File:Eristalis_tenax_auf_Tragopogon_pratensis_01.JPG" rel="noopener noreferrer"&gt;wikimedia commons&lt;/a&gt;), and to the Mapbox Standard Style below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhlmoggu6ciy7fvicdtqt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhlmoggu6ciy7fvicdtqt.png" alt=" " width="799" height="296"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ox0j9sd5rtr5bluqay6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ox0j9sd5rtr5bluqay6.png" alt=" " width="800" height="351"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Try this &lt;a href="https://lut.tgratzer.com/" rel="noopener noreferrer"&gt;handy LUT tool&lt;/a&gt; by Tony Gratzer to see how different LUTs affect an input image.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where Do LUTs Come From?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Download one
&lt;/h3&gt;

&lt;p&gt;There's a whole ecosystem of pre-made LUTs available online — similar to stock photography. Sites like &lt;a href="https://fixthephoto.com/free-luts" rel="noopener noreferrer"&gt;Fixthephoto&lt;/a&gt; and &lt;a href="https://www.rocketstock.com/free-after-effects-templates/35-free-luts-for-color-grading-videos/" rel="noopener noreferrer"&gt;RocketStock&lt;/a&gt; offer free and paid collections. Searching for "free HALD CLUT" will surface many more.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build one in Photoshop
&lt;/h3&gt;

&lt;p&gt;Photoshop's color grading tools — Curves, Hue/Saturation, Color Balance — can be applied to a standard identity LUT image and then exported. If you're working with a designer, this is a natural fit. Mapbox has a step-by-step tutorial here: &lt;a href="https://docs.mapbox.com/help/tutorials/create-a-custom-color-theme/" rel="noopener noreferrer"&gt;Create a custom color theme with Photoshop&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build one in the browser
&lt;/h3&gt;

&lt;p&gt;Several web-based tools let you create LUTs without any desktop software. &lt;a href="https://o-l-l-i.github.io/lut-maker/" rel="noopener noreferrer"&gt;LUT Maker by o-l-l-i&lt;/a&gt; provides sliders for hue rotation, saturation, brightness, contrast, and more, and exports the result as a PNG.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Challenge: Iterating on Custom Themes in a Mapbox Map
&lt;/h2&gt;

&lt;p&gt;When I set out to learn custom theming in Mapbox Standard, I arrived at what felt like the natural iteration loop:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open a browser-based LUT editor and adjust the sliders&lt;/li&gt;
&lt;li&gt;Export the LUT as a PNG&lt;/li&gt;
&lt;li&gt;Base64-encode it in the terminal: &lt;code&gt;base64 -i my-lut.png | pbcopy&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Open the &lt;a href="https://labs.mapbox.com/labs/standard-style-playground/" rel="noopener noreferrer"&gt;Mapbox Standard Style Playground&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Paste the string into the theme field&lt;/li&gt;
&lt;li&gt;Look at the result on the map&lt;/li&gt;
&lt;li&gt;Go back to step 1&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This worked. But it was tedious. Each iteration meant downloading a file, switching to the terminal, running a command, switching windows, and pasting a very long string — just to preview one change. For something as tactile and visual as color grading, that friction destroys creative momentum.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix was obvious: put the LUT editor and the map in the same window.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's exactly what the Mapbox GL Theme Editor does.&lt;/p&gt;




&lt;h2&gt;
  
  
  Introducing the Mapbox GL Theme Editor
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://chriswhong.github.io/mapboxgl-theme-editor/" rel="noopener noreferrer"&gt;→ Try it now&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The tool is a single-page web app pairing a LUT generation UI with a live Mapbox Standard map. It's &lt;a href="https://github.com/chriswhong/mapboxgl-theme-editor" rel="noopener noreferrer"&gt;open source&lt;/a&gt; and hosted on GitHub Pages.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgq0u160e3tvjdx2dgw34.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgq0u160e3tvjdx2dgw34.png" alt=" " width="800" height="497"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Global controls
&lt;/h3&gt;

&lt;p&gt;The main panel applies broad color adjustments across the entire palette:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hue rotation&lt;/strong&gt; — shifts all colors around the color wheel&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Saturation&lt;/strong&gt; — makes colors more vivid or more muted&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Brightness&lt;/strong&gt; — lightens or darkens the overall output&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Contrast&lt;/strong&gt; — expands or compresses the tonal range&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Temperature&lt;/strong&gt; — pushes the palette warmer (golden) or cooler (blue)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Drag any slider and the LUT preview in the sidebar updates instantly — and so does the map.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyveevsual44w0y2apal9.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyveevsual44w0y2apal9.gif" alt=" " width="600" height="327"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Targeted color corrections
&lt;/h3&gt;

&lt;p&gt;For more surgical edits, the tool includes a color correction mode. An eyedropper lets you select a specific color on the map — the blue of the water, the tan of building facades, the green of parks — and remap it to a different hue. A tolerance slider controls how broadly neighboring colors are pulled along.&lt;/p&gt;

&lt;p&gt;This is the right tool when you want to make precise changes: darken just the water, push the buildings to a warmer gray, or give vegetation a more stylized tint — without affecting anything else. The image below shows a targeted color correction changing water from light blue to light violet with low tolerance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; The Mapbox Standard Style is in active development and default colors could change in the future. An LUT with targeted color corrections may not apply as expected if the input color changes in a future release of Mapbox Standard.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff7cotojdodgxlkjtuzqt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff7cotojdodgxlkjtuzqt.png" alt=" " width="800" height="492"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Using Your Theme
&lt;/h2&gt;

&lt;p&gt;You can use the LUT you create in the theme editor in your real-world map development:&lt;/p&gt;

&lt;h3&gt;
  
  
  In Mapbox Studio
&lt;/h3&gt;

&lt;p&gt;In Mapbox Studio, apply a custom LUT by navigating to the Standard style's theme settings, setting the theme to "Custom" and uploading your LUT as an image file. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4bngkus0b46dtjmv1k53.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4bngkus0b46dtjmv1k53.png" alt=" " width="799" height="506"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  In the Standard Style Playground
&lt;/h3&gt;

&lt;p&gt;You can also test directly in the &lt;a href="https://labs.mapbox.com/labs/standard-style-playground/" rel="noopener noreferrer"&gt;Mapbox Standard Style Playground&lt;/a&gt; by setting the theme to custom and pasting in the base64 encoded string for your LUT. You can combine your custom theme with other Standard configuration options — toggling 3D buildings, point-of-interest labels, light presets — and see how they interact.&lt;/p&gt;

&lt;p&gt;The Standard Style Playground also provides handy code snippets for applying your theme at runtime in Mapbox GL JS or the Mapbox Mobile Maps SDKs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhbgnyez5oedr5826bf1p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhbgnyez5oedr5826bf1p.png" alt=" " width="799" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  In your frontend code
&lt;/h3&gt;

&lt;p&gt;You can use the base64 encoded string in your frontend code when using Mapbox GL JS or the Maps SDKs for iOS and Android along with the &lt;code&gt;theme&lt;/code&gt; and &lt;code&gt;theme-data&lt;/code&gt; Mapbox Standard Style config properties.&lt;/p&gt;

&lt;p&gt;Here's an example of how to instantiate a web map using the Mapbox Standard Style and a custom theme in a web project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;mapboxgl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;container&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;map&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mapbox://styles/mapbox/standard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;basemap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;custom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;theme-data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;YOUR_LUT_STRING&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;73.99059&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;40.74012&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;zoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;11.50&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Why Build a Custom Tool?
&lt;/h2&gt;

&lt;p&gt;Beyond solving the iteration problem, there was an educational motivation for building this.&lt;/p&gt;

&lt;p&gt;LUTs are unfamiliar territory for most web and mobile developers. The best way to build intuition for how they work is to &lt;em&gt;play with one&lt;/em&gt; — to drag a hue slider and immediately see every green park on the map turn magenta, or to pick the water with an eyedropper and watch the rivers shift from steel blue to teal. The tool makes an abstract concept concrete in a way that documentation simply can't.&lt;/p&gt;

&lt;p&gt;It also demystifies the pipeline. When the LUT preview image updates in the same window as the map, you can see exactly what a LUT is and what it does. You start to understand &lt;em&gt;why&lt;/em&gt; cranking contrast makes building shadows punch harder, or why rotating the hue by 180 degrees turns a daylight city into something that looks like a photo negative. That understanding makes you a more intentional user of the feature.&lt;/p&gt;

&lt;p&gt;On the practical side, the faster loop changes the quality of the work. Color choices that used to require five minutes of file-downloading and clipboard gymnastics now take five seconds of slider dragging. That's not just a time savings — it's the difference between exploring ten variations and exploring a hundred.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;This is a prototype. It works and it's fun to use, but there's room to grow. The repo is open source at &lt;a href="https://github.com/chriswhong/mapboxgl-theme-editor" rel="noopener noreferrer"&gt;github.com/chriswhong/mapboxgl-theme-editor&lt;/a&gt;. Issues and pull requests are welcome. And there's a conversation worth having about whether this kind of functionality should eventually live inside Mapbox Studio or the Standard Style Playground — or whether the complexity of a dedicated theming workflow makes it better as its own tool.&lt;/p&gt;




&lt;h2&gt;
  
  
  Give It a Try
&lt;/h2&gt;

&lt;p&gt;The best way to understand LUTs and custom theming in Mapbox Standard is to open the tool and start dragging sliders. It takes about thirty seconds before you're looking at a map that doesn't look like any map you've seen before.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://chriswhong.github.io/mapboxgl-theme-editor/" rel="noopener noreferrer"&gt;Open the Mapbox Standard Style Theme Editor →&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you find a look you like, grab the base64 string and drop it into your project — or share a screenshot and tell me what you made.&lt;/p&gt;

</description>
      <category>design</category>
      <category>showdev</category>
      <category>tooling</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How Lemontree improved map performance with a small refactor</title>
      <dc:creator>Chris Whong</dc:creator>
      <pubDate>Fri, 13 Feb 2026 21:51:45 +0000</pubDate>
      <link>https://dev.to/mapbox/how-lemontree-improved-map-performance-with-a-small-refactor-2272</link>
      <guid>https://dev.to/mapbox/how-lemontree-improved-map-performance-with-a-small-refactor-2272</guid>
      <description>&lt;p&gt;&lt;em&gt;A Q&amp;amp;A with &lt;a href="https://www.linkedin.com/in/samuelcole" rel="noopener noreferrer"&gt;Samuel Cole&lt;/a&gt;, CTO of Lemontree&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://www.foodhelpline.org/" rel="noopener noreferrer"&gt;Lemontree&lt;/a&gt; is a New York-based nonprofit on a mission to fight food insecurity by connecting hungry Americans to free food resources near them. Often described as a "Yelp for food banks," they curate and manage rich data on thousands of food pantries across the U.S., making it accessible through an easy-to-use digital platform with maps, SMS alerts, and more. In 2025, Lemontree helped &lt;strong&gt;over a million people&lt;/strong&gt; locate nearby food pantries.&lt;/p&gt;

&lt;p&gt;At the heart of their product is an interactive map — powered by &lt;a href="https://docs.mapbox.com/mapbox-gl-js/" rel="noopener noreferrer"&gt;Mapbox GL JS&lt;/a&gt; — that lets users instantly see nearby pantry locations, hours, and available foods. Recently, Lemontree overhauled how they load and display map data to dramatically improve performance. We sat down with Samuel Cole, CTO of Lemontree, to learn how they approached the problem and what they'd do differently from the start.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fenqn0js9pqccyuy1i90p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fenqn0js9pqccyuy1i90p.png" alt=" " width="800" height="487"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Chris:&lt;/strong&gt; Let's start with the original setup. What were you displaying on the map, where did the data come from, and what was falling short in terms of user experience?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Samuel:&lt;/strong&gt; When we load a map, we first call an endpoint to find the best 10 resources for the focus point of the map using our AI sorting algorithm, but then we don't want you to miss out on the other resources in the area, so we built an API called &lt;code&gt;markersWithinBounds&lt;/code&gt; that returns &lt;em&gt;all the resources&lt;/em&gt; within the bounding box of the map. These are visualized as many smaller background markers in addition to the larger "best 10 resources" markers.&lt;/p&gt;

&lt;p&gt;We're using &lt;a href="https://visgl.github.io/react-map-gl/" rel="noopener noreferrer"&gt;&lt;code&gt;react-map-gl&lt;/code&gt;&lt;/a&gt; so it was straightforward to loop over the results of that api call with a &lt;code&gt;.map()&lt;/code&gt; and create a component for each one. When we first built that, we were testing pretty zoomed in to a few blocks around the location, so while the density of food pantries is pretty high (there are more food pantries in the US than McDonalds!), it seemed like it was scaling ok, because most people are only interested in the immediate area. &lt;/p&gt;

&lt;p&gt;But in real life, pretty much everybody immediately zoomed out! All of a sudden the API call returned thousands of points, and rendering thousands of Markers on the screens was chugging. Users' whole device was lagging and slow, and then if they started panning around? It was wild.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fayjq7gfmdnidd1cc0uae.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fayjq7gfmdnidd1cc0uae.png" alt=" " width="800" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Zooming out on the map triggered an API call for new data and could contain hundreds of points or more.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Chris:&lt;/strong&gt; This is a classic Mapbox GL JS pitfall that catches a lot of developers by surprise. Markers are the most intuitive way to get point features on a map — the API is simple, positioning is easy, and you can drop in a custom image without much fuss. But they're rendered as individual DOM elements, which means &lt;strong&gt;performance degrades quickly as the count climbs&lt;/strong&gt;. Beyond a few hundred markers, you're fighting the browser itself. After you hit this wall, what solutions did you explore, and how did you arrive at the right path forward?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Samuel:&lt;/strong&gt; When I was preparing &lt;a href="//youtube.com/watch?si=5cvDlGFkV4LClk41&amp;amp;v=Axpahz-KpT0&amp;amp;feature=youtu.be"&gt;my talk for the BUILD with Mapbox&lt;/a&gt; virtual conference, you actually pulled me aside and said: "Hey, if you used a symbol layer for this it would scale a ton better."&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Chris:&lt;/strong&gt; Ah, well you were in the right place to get a direct suggestion! Hopefully this post will help a lot more people who might be hitting the same issue. Symbol layers are rendered by the GPU as part of the map's WebGL canvas, which means they handle thousands of points with ease and pan or zoom without any jank. The tradeoff is that they're less intuitive to set up, especially in a React codebase where you're used to thinking declaratively. Can you walk us through what the refactor actually looked like?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Samuel:&lt;/strong&gt; We have a lot going on at Lemontree! I wasn't able 📍 immediately prioritize that work, but one Friday I took it on as sort of a 'hack day' project. Unfortunately, I got a bit stuck because the &lt;code&gt;react-map-gl&lt;/code&gt; &lt;code&gt;&amp;lt;Layer&amp;gt;&lt;/code&gt; component, which was declarative in the React style, was being added before the style was loaded, and I just ran out of time in my hack day to figure it out. When I got access to AI-assisted development through Copilot and Claude Code, I was looking for little test cases, and I remembered this performance issue: I asked Claude, it added an &lt;code&gt;styleLoaded &amp;amp;&amp;amp;&lt;/code&gt; pattern, and we were off to the races! You can compare the code for the Marker implementation vs the Symbol layer:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before: Markers 📍&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Old approach: Render a DOM marker for each resource */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;otherMarkersData&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;features&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Marker&lt;/span&gt;
      &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;geometry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;coordinates&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;geometry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;coordinates&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Image&lt;/span&gt;
        &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;purplePin&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Resource"&lt;/span&gt;
        &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pointer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/resources/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Marker&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;))}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creates a DOM node for every marker (100s+ of elements)&lt;/li&gt;
&lt;li&gt;Heavy re-renders when panning/zooming&lt;/li&gt;
&lt;li&gt;Poor performance with many markers&lt;/li&gt;
&lt;li&gt;Limited styling options&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;After: Symbol Layer 🚀&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* New approach: Single GeoJSON source with symbol layer */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Source&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"other-resources"&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"geojson"&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;filteredOtherMarkersData&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="na"&gt;promoteId&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;
  &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Layer&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"other-resources-layer"&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"symbol"&lt;/span&gt;
      &lt;span class="na"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;icon-image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;purpleMarker&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;icon-allow-overlap&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Source&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Single GeoJSON source for all markers&lt;/li&gt;
&lt;li&gt;GPU-accelerated rendering&lt;/li&gt;
&lt;li&gt;Smooth performance with 1000+ markers&lt;/li&gt;
&lt;li&gt;Feature state for hover effects&lt;/li&gt;
&lt;li&gt;Click/hover handled via interactiveLayerIds&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Performance Impact:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;10x faster rendering&lt;/li&gt;
&lt;li&gt;Butter-smooth panning and zooming&lt;/li&gt;
&lt;li&gt;Reduced memory footprint&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Chris:&lt;/strong&gt; That's a great example of AI tooling unblocking a task that kept slipping down the priority list. The &lt;code&gt;styleLoaded&lt;/code&gt; guard is a small but critical detail — symbol layers depend on the map style being fully initialized before you can add sources or layers to the canvas at runtime, and it's an easy thing to miss until things break in subtle ways. What has the impact been since shipping? Are users noticing the difference?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Samuel:&lt;/strong&gt; Our clients are spending a bit more time exploring around the map, but honestly our recommendation algorithm gives plenty of food pantries near where they live, so in surprising ways exploring the map isn't their primary way of discovering food resources, and they use the map more for navigating the pantries we've already referred them to. Where the map really shines is with our partners! Organizations that run many pantries love to see their whole network, foundations love to see our coverage, and governments are interested in how food access is working in their districts.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Chris:&lt;/strong&gt; That's a fascinating insight — power users for the map include the partner organizations doing network-level planning, not just the end users trying to find nearby food. It reframes what "map performance" means for your product. Speaking of which, you've been building on Mapbox for a long time. What's your overall take on the platform — what's working well, and where do you think there's room to grow?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Samuel:&lt;/strong&gt; We love Mapbox; I've been using maps for my whole career, ever since my very first tech startup job, and Mapbox reflects my values for open, accessible, and extensible maps. The reality is that other mapping providers just don't offer the extensibility of Mapbox's platform, whenever I want to create a more targeted experience (which I believe people experiencing food insecurity deserve the best experiences!) the out-of-the-box approach from other map providers, just isn't going to cut it. I don't want a Google Map, I want a Lemontree Map.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Chris:&lt;/strong&gt; "I don't want a Google Map, I want a Lemontree Map" — I love that. Mapbox gives you all you need to make the whole map experience your own creation. What's on the roadmap next for mapping at Lemontree?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Samuel:&lt;/strong&gt; I love maps! Around Lemontree, my coworkers all call me the map guy. I think as we grow, it's going to be even more important for our partners to visualize how food access works around the U.S., so I'm excited to make more visualizations to tell stories like "Where are the food pantries that focus on elderly people in New Jersey, and where are the gaps?" There are almost 50 million people who experience food insecurity in the US, and we have a lot of work to do to understand where they are, and what resources they have available to them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chris:&lt;/strong&gt; Great, hopefully Mapbox maps can help with that data storytelling to raise awareness about food insecurity. Thanks for chatting with me about your map's technical hiccup and the fix. I'm glad to hear the map is performing better, especially given the importance of the data you're displaying on it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;p&gt;Lemontree's Markers to symbol layer refactor is a great case study in a problem many Mapbox developers encounter: &lt;strong&gt;Markers and symbol layers are both valid tools for displaying point data, but they have very different performance profiles.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Markers are DOM elements — straightforward to set up, but expensive to render at scale. Symbol layers are drawn by WebGL on the map's canvas, making them capable of displaying tens of thousands of points without breaking a sweat. If you're showing dynamic data that can grow beyond a few hundred points, a symbol layer is almost always the right call.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdbi3drm22imwbr9hu0ck.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdbi3drm22imwbr9hu0ck.gif" alt=" " width="600" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff64mdi5k3fx7dow3culn.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff64mdi5k3fx7dow3culn.gif" alt=" " width="600" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;These graphics help explain the difference between Markers, which are DOM elements displayed "on the map" versus symbol layers which are rendered "in the map"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For teams building in React with &lt;code&gt;react-map-gl&lt;/code&gt;, the key implementation detail is guarding your layer additions behind a &lt;code&gt;styleLoaded&lt;/code&gt; check — a small fix that, as Samuel found, can make the difference between a polished experience and a broken one. (the equivalent when using Mapbox GL JS without a wrapping library is listening for the &lt;a href="https://docs.mapbox.com/mapbox-gl-js/api/map/#map.event:style.load" rel="noopener noreferrer"&gt;&lt;code&gt;map.on(style.load)&lt;/code&gt;&lt;/a&gt; event. &lt;/p&gt;

&lt;p&gt;You can learn more about when to use Markers versus symbol layers in the &lt;a href="https://docs.mapbox.com/mapbox-gl-js/guides/" rel="noopener noreferrer"&gt;Add your Data guide&lt;/a&gt; in the Mapbox GL JS documentation.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>mapbox</category>
      <category>webmap</category>
      <category>community</category>
    </item>
    <item>
      <title>Squarespace X Mapbox</title>
      <dc:creator>Chris Whong</dc:creator>
      <pubDate>Thu, 12 Feb 2026 14:48:12 +0000</pubDate>
      <link>https://dev.to/mapbox/squarespace-x-mapbox-1k44</link>
      <guid>https://dev.to/mapbox/squarespace-x-mapbox-1k44</guid>
      <description>&lt;h1&gt;
  
  
  How to Add an Interactive Mapbox Map to Your Squarespace Website
&lt;/h1&gt;

&lt;p&gt;This guide shows developers how to integrate &lt;a href="https://docs.mapbox.com/mapbox-gl-js" rel="noopener noreferrer"&gt;Mapbox GL JS&lt;/a&gt; into a &lt;a href="https://squarespace.com" rel="noopener noreferrer"&gt;Squarespace&lt;/a&gt; website using Squarespace's &lt;em&gt;embed block&lt;/em&gt;. If you're already familiar with Mapbox GL JS development and want to add custom interactive maps to your Squarespace site, this post will show you exactly where to place your code to get it working.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅ What this guide covers:&lt;/strong&gt; The technical steps to embed Mapbox GL JS code in a Squarespace website &lt;br&gt;
&lt;strong&gt;❌ What this guide doesn't cover:&lt;/strong&gt; Details of interactive map development with Mapbox GL JS (the &lt;a href="https://docs.mapbox.com/mapbox-gl-js/exmaples" rel="noopener noreferrer"&gt;examples&lt;/a&gt; are a great place to start for inspiration and working code snippets)&lt;/p&gt;

&lt;p&gt;We'll show a &lt;strong&gt;simple example&lt;/strong&gt; of displaying a business location with a marker, popup, and rotating camera to demonstrate the integration. Once you understand where the code goes and how it's structured in Squarespace, you can implement any Mapbox GL JS functionality you need by changing the JavaScript code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbbxl46sarfeguckfn8sm.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbbxl46sarfeguckfn8sm.gif" alt=" " width="600" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's a codepen so you can get a better look at the actual map and animation shown in this guide. It uses the &lt;a href="https://docs.mapbox.com/map-styles/standard/guides/" rel="noopener noreferrer"&gt;Mapbox Standard Style&lt;/a&gt; as a basemap, showing detailed 3D buildings and an optional monochrome theme. The popup is styled to match the green colors used on the Squarespace site it is embedded in:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/Chris-Whong/embed/NPrBPeb?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This technique of adding custom HTML, CSS and JavaScript via the Squarespace editor is suitable for simpler Mapbox GL JS implementations but does not include version control or other benefits of a separate development workflow. If your map becomes more complex, you may want to deploy it as a standalone web app, then embed it in your Squarespace site using its URL. &lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;To follow this guide, you'll need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A Squarespace website&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Working knowledge of Mapbox GL JS&lt;/strong&gt; - you should be comfortable writing HTML, CSS, and JavaScript to create maps&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A Mapbox account and access token&lt;/strong&gt; - if you don't have one yet, &lt;a href="https://account.mapbox.com" rel="noopener noreferrer"&gt;sign up here&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting Your Mapbox Access Token
&lt;/h2&gt;

&lt;p&gt;Before we start, you'll need a &lt;strong&gt;Mapbox access token&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://account.mapbox.com" rel="noopener noreferrer"&gt;mapbox.com&lt;/a&gt; and sign up for a free account (if you don't have one)&lt;/li&gt;
&lt;li&gt;Once logged in, navigate to your &lt;a href="https://console.mapbox.com" rel="noopener noreferrer"&gt;console&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Find the "Tokens" link in the left sidebar to go to the &lt;a href="https://console.mapbox.com/account/access-tokens/" rel="noopener noreferrer"&gt;tokens page&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Copy your &lt;strong&gt;default public token&lt;/strong&gt; (it starts with &lt;code&gt;pk.&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Keep this token handy—you'll need it in a moment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Adding the Code Block
&lt;/h2&gt;

&lt;p&gt;Let's add the HTML that will contain your map code:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open your Squarespace Editor and navigate to the page where you want to add the map&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;+ ADD BLOCK&lt;/strong&gt; button
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fefeycfqv5ivli77uua9x.png" alt=" " width="800" height="496"&gt;
&lt;/li&gt;
&lt;li&gt;Scroll down in the block selection panel to find "Embed"
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsazbuvwsimthgyby51oe.png" alt=" " width="800" height="525"&gt;
&lt;/li&gt;
&lt;li&gt;Drag and resize the embed block to position it wherever you want it on your site layout.
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fscijvvhcunv1iypzcczg.gif" alt=" " width="799" height="467"&gt;
&lt;/li&gt;
&lt;li&gt;Double-click on the embed block to open its &lt;strong&gt;Content&lt;/strong&gt; settings panel. Under &lt;strong&gt;EMBED AS&lt;/strong&gt;, choose &lt;strong&gt;Code Snippet&lt;/strong&gt;, then click the &lt;strong&gt;Embed Data&lt;/strong&gt; button. 
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbz6qz59qjf9rqfj0qy44.png" alt=" " width="799" height="467"&gt;
&lt;/li&gt;
&lt;li&gt;A code editor will appear. You will add code to this input in the next step.
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fudlyirvwczahun8rxwkd.png" alt=" " width="800" height="508"&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 2: Add Mapbox GL JS Code
&lt;/h2&gt;

&lt;p&gt;Now you'll add Mapbox GL JS code to get a map working in the code block. The code block accepts any HTML, so you can include CSS and JavaScript by using &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags.&lt;/p&gt;

&lt;p&gt;Here's a working example that shows a business location with a marker and rotating camera. Copy this code and replace &lt;code&gt;YOUR_MAPBOX_ACCESS_TOKEN&lt;/code&gt; with your actual access token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- import Mapbox GL JS and its CSS file --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;'https://api.mapbox.com/mapbox-gl-js/v3.18.1/mapbox-gl.js'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;'https://api.mapbox.com/mapbox-gl-js/v3.18.1/mapbox-gl.css'&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;'stylesheet'&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- override default styles for the map and popup --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
  &lt;span class="nc"&gt;.mapboxgl-popup-content&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#e6f2d2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#2f5f48&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- add a container element for the map --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"map-container"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"position: absolute; height: 100%; width: 100%"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="c1"&gt;// your Mapbox access token&lt;/span&gt;
  &lt;span class="nx"&gt;mapboxgl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accessToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR_MAPBOX_ACCESS_TOKEN&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// initialize the map&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;mapboxgl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;container&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;map-container&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// container ID&lt;/span&gt;
    &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mapbox://styles/mapbox/standard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// style URL&lt;/span&gt;
    &lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;73.99986&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;40.75300&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// starting position [lng, lat]&lt;/span&gt;
    &lt;span class="na"&gt;zoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;15.36&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// starting zoom&lt;/span&gt;
    &lt;span class="na"&gt;bearing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;28.00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// bearing in degrees&lt;/span&gt;
    &lt;span class="na"&gt;pitch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;56.50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// pitch in degrees&lt;/span&gt;
    &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;basemap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;monochrome&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// use the monochrome basemap theme&lt;/span&gt;
        &lt;span class="na"&gt;showPointOfInterestLabels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="c1"&gt;// hide point of interest labels&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// disable scroll zoom so the map won't zoom as the user scrolls down the page&lt;/span&gt;
  &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scrollZoom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disable&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// create a popup with address details and a link to get directions on Google Maps&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;popup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;mapboxgl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Popup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;closeOnClick&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;closeButton&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;setHTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`&amp;lt;div&amp;gt;
      450 W 33rd St&amp;lt;br/&amp;gt;
      New York, NY 10001&amp;lt;/div&amp;gt;
      &amp;lt;a href="https://maps.app.goo.gl/6WzErvMBMi2Hn64Z7" target="_blank" rel="noopener noreferrer"&amp;gt;&amp;lt;span class="text-xs"&amp;gt;Get Directions&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;
    `&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// create a map marker with a custom color, connect the popup, add it to the map, and open the popup&lt;/span&gt;
  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;mapboxgl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Marker&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#2f5f48&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setLngLat&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;73.99986&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;40.75300&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setPopup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;popup&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;togglePopup&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// this function rotates the camera continuously&lt;/span&gt;
  &lt;span class="c1"&gt;// see https://docs.mapbox.com/mapbox-gl-js/example/animate-camera-around-point/&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;rotateCamera&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rotateTo&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;360&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nf"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rotateCamera&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;load&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// start the camera animation.&lt;/span&gt;
    &lt;span class="nf"&gt;rotateCamera&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Paste the Mapbox GL JS snippet from above into the code editor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;: Replace the placeholder text &lt;code&gt;YOUR_MAPBOX_ACCESS_TOKEN&lt;/code&gt; with the actual token you copied from your Mapbox account.&lt;/p&gt;

&lt;p&gt;Click anywhere outside the editor panel to apply your code update. You will see a warning about embedded scripts, with an option to &lt;strong&gt;preview in safe mode&lt;/strong&gt;. This is normal, as Squarespace prevents execution of JavaScript code in the editor by default. &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo9xzfdggqxcy90hjutqy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo9xzfdggqxcy90hjutqy.png" alt=" " width="800" height="472"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Exit the editor. You should see an animated map with a monochrome basemap, a marker centered over New York City, and a slowly rotating camera. That's it, you're ready to publish this map on your site!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5ro3k7gwa759ao8p1pki.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5ro3k7gwa759ao8p1pki.png" alt=" " width="800" height="492"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Understanding the Code Structure
&lt;/h2&gt;

&lt;p&gt;Here's how the code for this example map breaks down:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Library Imports&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;'https://api.mapbox.com/mapbox-gl-js/v3.18.1/mapbox-gl.js'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;'https://api.mapbox.com/mapbox-gl-js/v3.18.1/mapbox-gl.css'&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;'stylesheet'&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These load Mapbox GL JS and its required stylesheet from the Mapbox CDN.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Custom Styles&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
  &lt;span class="nc"&gt;.mapboxgl-popup-content&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#e6f2d2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#2f5f48&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The popup styles customize the appearance to match the page theme.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Map Container&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"map-container"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"position: absolute; height: 100%; width: 100%"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This div is where the map renders. The inline styles ensure it fills the available space.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Your Map Logic&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="c1"&gt;// All your Mapbox GL JS code goes here&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is where you implement your map functionality. In this example, we're:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setting the access token&lt;/li&gt;
&lt;li&gt;Initializing the map with specific style and camera settings&lt;/li&gt;
&lt;li&gt;Disabling scroll zoom (important for Squarespace pages!)&lt;/li&gt;
&lt;li&gt;Creating a popup and marker&lt;/li&gt;
&lt;li&gt;Adding a camera rotation animation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can replace or extend this code with any Mapbox GL JS functionality you need. &lt;/p&gt;

&lt;p&gt;If you want to adapt this example of a single marker and popup, you should:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Change the map's center point, zoom, pitch and bearing to your desired initial camera view.&lt;/li&gt;
&lt;li&gt;Change the Marker's coordinates&lt;/li&gt;
&lt;li&gt;Change the Popup's content by editing the HTML code in &lt;code&gt;Popup.setHTML()&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Or, you can leave the imports and the map container in place and start over with the map JavaScript code and build whatever you need. See additional developer resources below.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources for Mapbox GL JS Development
&lt;/h2&gt;

&lt;p&gt;Here are essential resources for building your map functionality:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mapbox Documentation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.mapbox.com/mapbox-gl-js/example/" rel="noopener noreferrer"&gt;Mapbox GL JS Examples&lt;/a&gt; - Dozens of live, interactive code examples&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.mapbox.com/mapbox-gl-js/" rel="noopener noreferrer"&gt;Mapbox GL JS Guides&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.mapbox.com/mapbox-gl-js/api/" rel="noopener noreferrer"&gt;Mapbox GL JS API Reference&lt;/a&gt; - Complete documentation of all map methods and options&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Key Examples&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.mapbox.com/mapbox-gl-js/example/simple-map/" rel="noopener noreferrer"&gt;Display a map&lt;/a&gt; - Basic map initialization&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.mapbox.com/mapbox-gl-js/example/add-a-marker/" rel="noopener noreferrer"&gt;Add markers to a map&lt;/a&gt; - Working with markers&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.mapbox.com/mapbox-gl-js/example/popup/" rel="noopener noreferrer"&gt;Display a popup&lt;/a&gt; - Popup functionality&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.mapbox.com/mapbox-gl-js/example/geojson-markers/" rel="noopener noreferrer"&gt;Add GeoJSON data&lt;/a&gt; - Display custom data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Community&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://reddit.com/r/mapbox" rel="noopener noreferrer"&gt;r/mapbox on Reddit&lt;/a&gt; - Get help and share ideas&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://discord.com/invite/mapboxdevs-1004826913229000704" rel="noopener noreferrer"&gt;Mapbox Developer Discord&lt;/a&gt; - Post in the &lt;code&gt;#web&lt;/code&gt; channel to ask questions and share what you build.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Map not displaying?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Verify you've replaced &lt;code&gt;YOUR_MAPBOX_ACCESS_TOKEN&lt;/code&gt; with your actual token&lt;/li&gt;
&lt;li&gt;Check that your access token has the correct permissions&lt;/li&gt;
&lt;li&gt;Ensure the code block has sufficient height and width in the Squarespace editor &lt;strong&gt;and&lt;/strong&gt; that the map container div also has sufficient height and width&lt;/li&gt;
&lt;li&gt;Open your browser's developer console to check for JavaScript errors&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;By following the steps in this guide, you should now have a Mapbox GL JS map working in your Squarespace website and are ready to iterate the JavaScript code to customize the map to meet your needs.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Questions about this integration approach? Found a better way to do something? Share your experience in the comments!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>squarespace</category>
      <category>mapbox</category>
      <category>webmap</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
