<?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>Forem: Chris Whong</title>
    <description>The latest articles on Forem by Chris Whong (@chriswhong).</description>
    <link>https://forem.com/chriswhong</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3616416%2Fd733d6e1-b559-4d01-a900-3e3132db7e75.jpg</url>
      <title>Forem: Chris Whong</title>
      <link>https://forem.com/chriswhong</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/chriswhong"/>
    <language>en</language>
    <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://forem.com/mapbox/a-gui-editor-for-mapbox-color-themes-5fho</link>
      <guid>https://forem.com/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="797"&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="800" 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="350"&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="720" height="391"&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="800" 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="800" 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://forem.com/mapbox/how-lemontree-improved-map-performance-with-a-small-refactor-2272</link>
      <guid>https://forem.com/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="486"&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="539"&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="720" height="402"&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="720" height="504"&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://forem.com/mapbox/squarespace-x-mapbox-1k44</link>
      <guid>https://forem.com/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=" "&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=" "&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=" "&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=" "&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=" "&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=" "&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=" "&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=" "&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>
    <item>
      <title>Mapbox Demo: Bike route visualization color gradient and data scrubbing UX</title>
      <dc:creator>Chris Whong</dc:creator>
      <pubDate>Mon, 09 Feb 2026 18:45:07 +0000</pubDate>
      <link>https://forem.com/mapbox/mapbox-demo-bike-route-visualization-color-gradient-and-data-scrubbing-ux-9kb</link>
      <guid>https://forem.com/mapbox/mapbox-demo-bike-route-visualization-color-gradient-and-data-scrubbing-ux-9kb</guid>
      <description>&lt;p&gt;A developer on Reddit recently asked for &lt;a href="https://www.reddit.com/r/mapbox/comments/1qwg1ok/performance_advice_for_stravalike_ride_playback/" rel="noopener noreferrer"&gt;help building a ride playback feature&lt;/a&gt; with speed visualization on a Mapbox GL JS map. They had detailed GPS and speed data from a bike ride and wanted to show speed variations along the route. &lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge
&lt;/h2&gt;

&lt;p&gt;The original question centered on two main requirements:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Visualize speed variations&lt;/strong&gt; along the route with color-coded gradients (slow = green, medium = yellow, fast = red)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interactive scrubbing&lt;/strong&gt; that shows detailed speed and time data when the user hovers over any point on the route&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;Here's the working implementation on codepen. The line visualizes the variations in speed along the bike trip. Move your mouse over the line to "scrub" and see data for a specific spot on the line.&lt;/p&gt;

&lt;p&gt;

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


&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Implementation Details
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Speed Gradient&lt;/strong&gt;&lt;br&gt;
Instead of creating separate line segments for each speed zone, we use a single GeoJSON LineString with &lt;code&gt;lineMetrics: true&lt;/code&gt; and build a dynamic gradient using the &lt;code&gt;line-gradient&lt;/code&gt; paint 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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;line-gradient&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;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="s1"&gt;linear&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;line-progress&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="nx"&gt;gradientStops&lt;/span&gt;  &lt;span class="c1"&gt;// Dynamically generated from speed data&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each speed data point gets mapped to a position along the line (0 to 1) and assigned a color based on its speed value. Check out the &lt;a href="https://docs.mapbox.com/mapbox-gl-js/example/line-gradient/" rel="noopener noreferrer"&gt;official line-gradient example&lt;/a&gt; for more details on this technique.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A Note on Accuracy&lt;/strong&gt;&lt;br&gt;
This demo simplifies the gradient calculation by evenly distributing speed data points along the line based on timestamps. In a production app, you'd want to map each speed measurement to its actual GPS coordinate along the route for precise gradient placement. This ensures the gradient accurately reflects where speed changes occurred geographically, not just temporally.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interactive Scrubbing&lt;/strong&gt;&lt;br&gt;
The scrubber uses geometry calculations to find the nearest point on the route as you move your mouse, then interpolates between speed data points to show accurate values at that location. A pink circle marker follows your cursor along the route, displaying the time and speed in a floating tooltip.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance Benefits
&lt;/h2&gt;

&lt;p&gt;This approach renders a single line layer with a gradient instead of dozens of individual geometries, resulting in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Faster initial render&lt;/li&gt;
&lt;li&gt;Smooth interactions&lt;/li&gt;
&lt;li&gt;Lower memory usage&lt;/li&gt;
&lt;li&gt;Simpler state management&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Cross-Platform Compatibility
&lt;/h2&gt;

&lt;p&gt;While this example uses Mapbox GL JS for the web, the same &lt;code&gt;line-gradient&lt;/code&gt; technique works on &lt;a href="https://docs.mapbox.com/ios/maps/guides/" rel="noopener noreferrer"&gt;iOS&lt;/a&gt; and &lt;a href="https://docs.mapbox.com/android/maps/guides/" rel="noopener noreferrer"&gt;Android&lt;/a&gt; using the native Maps SDKs. The gradient expression syntax is consistent across all platforms.&lt;/p&gt;

&lt;p&gt;Check out the full code in the CodePen above to see how the speed interpolation and distance calculations work. Happy mapping!&lt;/p&gt;

</description>
      <category>mapbox</category>
      <category>dataviz</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Wix X Mapbox</title>
      <dc:creator>Chris Whong</dc:creator>
      <pubDate>Wed, 04 Feb 2026 17:43:31 +0000</pubDate>
      <link>https://forem.com/mapbox/mapbox-x-wix-44j5</link>
      <guid>https://forem.com/mapbox/mapbox-x-wix-44j5</guid>
      <description>&lt;h1&gt;
  
  
  How to Add an Interactive Mapbox Map to Your Wix 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://wix.com" rel="noopener noreferrer"&gt;Wix&lt;/a&gt; website using the Wix editor's embed code feature. If you're already familiar with Mapbox GL JS development and want to add custom interactive maps to your Wix 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 Wix 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 Wix, 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%2Fx08aynzhpi9s7mimtoay.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%2Fx08aynzhpi9s7mimtoay.gif" alt=" "&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 Wix 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 Wix 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 Wix 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 Wix 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;
  
  
  How Wix Embeds Work
&lt;/h2&gt;

&lt;p&gt;Before we dive in, it's important to understand what's happening under the hood. When you add an embed element in the Wix editor, it becomes an &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/iframe" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt;&lt;/a&gt; in your published site. This iframe contains whatever HTML, CSS, and JavaScript you add to it—completely isolated from the rest of your Wix page.&lt;/p&gt;

&lt;p&gt;This isolation is actually a benefit: you have complete control over the content inside the iframe without worrying about conflicts with Wix's own code. You can implement any Mapbox GL JS functionality you want, exactly as you would in a standalone HTML page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Adding the Embed Element
&lt;/h2&gt;

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

&lt;ol&gt;
&lt;li&gt;Open your &lt;a href="https://editor.wix.com/" rel="noopener noreferrer"&gt;Wix Editor&lt;/a&gt; and navigate to the page where you want to add the map&lt;/li&gt;
&lt;li&gt;Click the "+ Add" button (in the top left) to add an element&lt;/li&gt;
&lt;li&gt;Scroll through the different elements and select "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%2Fb7ihd14cpf0v2zjrwwha.png" alt=" "&gt;
&lt;/li&gt;
&lt;li&gt;Choose "Embed Code" on the next panel
&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%2Fue3opef0ojes7uow8cwy.png" alt=" "&gt;
&lt;/li&gt;
&lt;li&gt;Drag and resize the embed element to your desired size on the page
&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%2Fvgn1rubtp944mp10nain.gif" alt=" "&gt;
&lt;/li&gt;
&lt;li&gt;Double click the embed element to open its Settings panel&lt;/li&gt;
&lt;/ol&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%2F7aqp0mrkm4h7rvdpnj13.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%2F7aqp0mrkm4h7rvdpnj13.png" alt=" "&gt;&lt;/a&gt;&lt;/p&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 embed. The embed 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="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
  &lt;span class="p"&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="nc"&gt;.mapboxgl-popup-tip&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;border-top-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#e6f2d2&lt;/span&gt; &lt;span class="cp"&gt;!important&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;Delete the existing code in the textarea labeled &lt;strong&gt;Enter HTML embed code&lt;/strong&gt; and paste the Mapbox GL JS snippet from above:&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%2Fn9akdgn51zsuya5dr9nl.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%2Fn9akdgn51zsuya5dr9nl.gif" alt=" "&gt;&lt;/a&gt;&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 "Apply" to save your code, then preview your site. You should see an interactive map with a monochrome basemap, a marker centered over New York City, and a slowly rotating camera.&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%2Fx08aynzhpi9s7mimtoay.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%2Fx08aynzhpi9s7mimtoay.gif" alt=" "&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 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="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
  &lt;span class="p"&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 &lt;code&gt;body { margin: 0 }&lt;/code&gt; removes default margins so the map fills the entire iframe. 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 Wix 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 embed element has sufficient height and width in the Wix 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 Wix 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>wix</category>
      <category>mapbox</category>
      <category>webmap</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Server-rendered Maps for Web and Mobile</title>
      <dc:creator>Chris Whong</dc:creator>
      <pubDate>Thu, 29 Jan 2026 16:42:21 +0000</pubDate>
      <link>https://forem.com/mapbox/server-rendered-maps-for-web-and-mobile-51f7</link>
      <guid>https://forem.com/mapbox/server-rendered-maps-for-web-and-mobile-51f7</guid>
      <description>&lt;h2&gt;
  
  
  A Developer's Guide to the Mapbox Static Images API
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.mapbox.com/maps" rel="noopener noreferrer"&gt;Interactive maps&lt;/a&gt; are a powerful user experience element — they let users pan, zoom, and explore wide geographic areas or interact with dense datasets. But there's a catch: &lt;em&gt;not every use case needs the full weight of a client-rendered map.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Rendering vector maps in the client requires loading &lt;strong&gt;all the map data&lt;/strong&gt; into the browser or device, which means significant network traffic, device processing overhead, and performance costs.&lt;/p&gt;

&lt;p&gt;Sometimes you just need &lt;strong&gt;a picture of a map&lt;/strong&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%2Fstsls686nqpuq1evfiir.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%2Fstsls686nqpuq1evfiir.png" alt="A static map image of Hyde Park in London, England" width="600" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If users are quickly scrolling past location information, or if you're displaying a long list where every item needs a map thumbnail, it doesn't make sense to bring in a vector map library or SDK and load lots of client-rendered data just to show a marker or two. &lt;/p&gt;

&lt;p&gt;That's where the &lt;a href="https://docs.mapbox.com/api/maps/static-images/" rel="noopener noreferrer"&gt;Mapbox Static Images API&lt;/a&gt; comes in.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is the Static Images API?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.mapbox.com/api/maps/static-images" rel="noopener noreferrer"&gt;The Mapbox Static Images API&lt;/a&gt; is an HTTP endpoint that returns map images. All you need is a center point (latitude and longitude) and a zoom level (0 is zoomed out to world level, 18 or so gets you to street level). This will serve up a beautiful map image from anywhere in the world — no JavaScript libraries, no SDK packages,  no client-side rendering, just a clean image URL.&lt;/p&gt;

&lt;p&gt;To help you get an understanding of the URL structure for a call to the Static Images API, here are some map images of landmarks and their associated Static Images API URLs. You can try the URLs yourself, just replace &lt;code&gt;YOUR_MAPBOX_ACCESS_TOKEN&lt;/code&gt; with an access token from &lt;a href="https://account.mapbox.com" rel="noopener noreferrer"&gt;your Mapbox account&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Eiffel Tower&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The site of the Eiffel Tower in Paris (2.295,48.8584), France, using the &lt;a href="https://docs.mapbox.com/api/maps/styles/#classic-mapbox-styles" rel="noopener noreferrer"&gt;Mapbox Streets&lt;/a&gt; style at zoom &lt;code&gt;12.77&lt;/code&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%2Fq4c7tl5kkp12dyqxqh0f.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%2Fq4c7tl5kkp12dyqxqh0f.png" alt="a static map image showing the Eiffel Tower site in Paris, France" width="800" height="600"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://api.mapbox.com/styles/v1/mapbox/streets-v12/static/2.295,48.8584,12.77,0/400x300@2x?access_token=YOUR_MAPBOX_ACCESS_TOKEN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The site of the Golden Gate Bridge (-122.4717,37.8177) in San Francisco, California, USA, using the &lt;a href="https://docs.mapbox.com/api/maps/styles/#classic-mapbox-styles" rel="noopener noreferrer"&gt;Mapbox Dark&lt;/a&gt; style at zoom &lt;code&gt;10.56&lt;/code&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%2F4qldnfp18ob2527o5jb3.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%2F4qldnfp18ob2527o5jb3.png" alt="a static map image showing the golden gate bridge site in San Francisco, California, USA" width="800" height="600"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://api.mapbox.com/styles/v1/mapbox/dark-v11/static/-122.4717,37.8177,10.56,0/400x300@2x?access_token=YOUR_MAPBOX_ACCESS_TOKEN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The site of the Sydney Opera House in Sydney, Australia, using the &lt;a href="https://docs.mapbox.com/api/maps/styles/#classic-mapbox-styles" rel="noopener noreferrer"&gt;Mapbox Satellite Streets&lt;/a&gt; style at zoom &lt;code&gt;13.63&lt;/code&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%2F5x16yv3y97qc5pzr418n.jpeg" 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%2F5x16yv3y97qc5pzr418n.jpeg" alt="a static map image showing the site of the Sydney Opera House in Sydney, Australia" width="800" height="600"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://api.mapbox.com/styles/v1/mapbox/satellite-streets-v12/static/151.215,-33.858,13.63,0/400x300@2x?access_token=YOUR_MAPBOX_ACCESS_TOKEN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the syntax for the URL structure is straightforward. Each of these images is 800x600 pixels (the request shows &lt;code&gt;400x300@2x&lt;/code&gt; for retina images), and include the longitude, latitude, zoom level, and style id.&lt;/p&gt;

&lt;p&gt;You can copy the URLs, add your own Mapbox access token and change out the longitude and latitude coordinates to get a map image for an area of interest to you. If you need to quickly get coordinates for a place, try our handy &lt;a href="https://labs.mapbox.com/location-helper/#3/40.78/-73.97" rel="noopener noreferrer"&gt;Location Helper&lt;/a&gt; tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Your Data with Overlays
&lt;/h2&gt;

&lt;p&gt;It's rare that you would ever want &lt;strong&gt;just a basemap&lt;/strong&gt;. The real power of the Static Images API comes from its overlays feature. Overlays let you add your own data — &lt;strong&gt;markers, lines, and polygons&lt;/strong&gt; — directly onto the map.&lt;/p&gt;

&lt;p&gt;The overlay system is completely flexible: you can use simple longitude and latitude coordinates, or bring in encoded linestrings or GeoJSON for more complex geometries. This makes it trivial to combine Mapbox's professional basemaps with your application's location data.&lt;/p&gt;

&lt;p&gt;Overlays use a DSL and end up as sections of the URL. For example, a small (&lt;code&gt;s&lt;/code&gt;) red (&lt;code&gt;#b44646&lt;/code&gt;) marker at coordinates &lt;code&gt;-0.1717,51.5068&lt;/code&gt; is encoded as &lt;code&gt;pin-s+b44646(-0.1717,51.5068)&lt;/code&gt;. Don't worry about the learning curve for encoding overlays, we made a &lt;a href="https://docs.mapbox.com/playground/static" rel="noopener noreferrer"&gt;Developer Playground&lt;/a&gt; to help you configure them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three Real-World Use Cases
&lt;/h2&gt;

&lt;p&gt;Below are three practical examples that showcase how to combine Mapbox basemaps with overlays from the Static Images API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Directions Route with Origin and Destination Markers&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A common use cases in location apps is showing directions and travel times from point A to point B. Whether you're building a ride-sharing app, a delivery service, or a travel planner, you need to visualize routes clearly.&lt;/p&gt;

&lt;p&gt;With the Static Images API, you can overlay a route &lt;a href="https://developers.google.com/maps/documentation/utilities/polylinealgorithm" rel="noopener noreferrer"&gt;polyline&lt;/a&gt; along with markers for the origin and destination. This gives users an instant visual understanding of the journey without requiring them to interact with a full map.&lt;/p&gt;

&lt;p&gt;Example:&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%2F9g8w0w9qyt2mco2pgzqj.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%2F9g8w0w9qyt2mco2pgzqj.png" alt="a static map image showing a directions route overlay and two markers for the origin and destination" width="800" height="533"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://api.mapbox.com/styles/v1/mapbox/streets-v12/static/path-7+6d8afd-0.8(ycmlFdweuM%3FglAsF%40B_CirAi%7CGkHyfG%7ByCspD%7B%7DGyfCyrAgsCenHcoCgbDylBiaHgkJ_lDabB_fGszFuxDqeJ%7BfCw%5Bq%7C%40%7BcAwcEczA_Aa%7D%40aR%5C%5C%3F),pin-l+51bd5e(-77.0393863890253,38.90253),pin-l+f54747(-76.612299,39.289643)/-76.7636,39.1125,8.45,0/600x400@2x?attribution=true&amp;amp;logo=true&amp;amp;access_token=YOUR_MAPBOX_ACCESS_TOKEN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Use an &lt;a href="https://developers.google.com/maps/documentation/utilities/polylineutility" rel="noopener noreferrer"&gt;encoded polyline&lt;/a&gt; overlay for the route geometry (The &lt;a href="https://docs.mapbox.com/api/navigation/directions/" rel="noopener noreferrer"&gt;Mapbox Directions API&lt;/a&gt; can return this format, that's where the geometry in this example came from)&lt;/li&gt;
&lt;li&gt;Add marker overlays for origin and destination with different colors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Property Listing with 15-Minute Accessibility Isochrone&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Real estate and rental platforms need to communicate location value effectively. Showing a home's location is helpful, but showing what's accessible from that location is even better.&lt;/p&gt;

&lt;p&gt;An isochrone is a polygon that represents all the areas reachable from a point within a certain time frame. (&lt;a href="https://docs.mapbox.com/api/navigation/isochrone" rel="noopener noreferrer"&gt;We have an API&lt;/a&gt; for that too!) For a property listing, overlaying a 15-minute isochrone shows potential buyers or renters exactly how far they can get by walking, biking, or driving — highlighting the home's convenience and accessibility.&lt;/p&gt;

&lt;p&gt;This approach could also show the delivery area for a business or any other relevant polygon like a school district.&lt;/p&gt;

&lt;p&gt;Example:&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%2Fu6rvcw17j8mxq996f0di.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%2Fu6rvcw17j8mxq996f0di.png" alt="a static map image showing a marker and a 10-minute travel time isochrone" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;https://api.mapbox.com/styles/v1/mapbox/dark-v11/static/geojson({"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-76.508577,42.514225],[-76.509957,42.513128],[-76.509123,42.508674],[-76.51144,42.504265],[-76.513577,42.502387],[-76.510127,42.500128],[-76.511751,42.498128],[-76.512271,42.488128],[-76.511283,42.487128],[-76.511306,42.483128],[-76.510324,42.480128],[-76.504437,42.472268],[-76.504167,42.470538],[-76.50139,42.467128],[-76.501189,42.464516],[-76.505282,42.461833],[-76.507577,42.46181],[-76.508577,42.459468],[-76.503577,42.45937],[-76.505579,42.457126],[-76.505802,42.454903],[-76.507577,42.453848],[-76.515345,42.45436],[-76.51784,42.452865],[-76.513262,42.450444],[-76.515998,42.448128],[-76.519319,42.453386],[-76.521577,42.455901],[-76.525577,42.452965],[-76.523118,42.451587],[-76.522831,42.449128],[-76.520577,42.449318],[-76.517225,42.445128],[-76.521577,42.443245],[-76.521074,42.438128],[-76.524371,42.436128],[-76.521759,42.434128],[-76.514694,42.434245],[-76.511158,42.433548],[-76.510105,42.430128],[-76.505996,42.428128],[-76.502577,42.428537],[-76.500033,42.432584],[-76.498334,42.429128],[-76.500068,42.428619],[-76.500394,42.425945],[-76.503844,42.423395],[-76.503226,42.418128],[-76.505035,42.417128],[-76.505053,42.412128],[-76.50193,42.410481],[-76.501943,42.417128],[-76.499577,42.418592],[-76.497577,42.417807],[-76.495368,42.419919],[-76.497856,42.421407],[-76.494862,42.424128],[-76.489532,42.425173],[-76.485245,42.42246],[-76.483325,42.418128],[-76.480532,42.417083],[-76.482577,42.421654],[-76.484577,42.423622],[-76.482577,42.426795],[-76.485577,42.428684],[-76.486205,42.4315],[-76.476577,42.43128],[-76.475212,42.428493],[-76.470462,42.427243],[-76.468432,42.425273],[-76.469064,42.422641],[-76.464577,42.42142],[-76.462577,42.422323],[-76.460151,42.419554],[-76.456427,42.418278],[-76.455088,42.416617],[-76.451321,42.415128],[-76.455346,42.410897],[-76.459955,42.408506],[-76.458864,42.407414],[-76.455822,42.409373],[-76.451921,42.410785],[-76.449577,42.412473],[-76.443373,42.411332],[-76.437577,42.4076],[-76.436152,42.409554],[-76.439795,42.41091],[-76.447756,42.414949],[-76.453774,42.420325],[-76.448577,42.420775],[-76.448577,42.426175],[-76.453577,42.426839],[-76.456253,42.429452],[-76.461745,42.43196],[-76.461577,42.434379],[-76.457577,42.434404],[-76.4523,42.432406],[-76.446577,42.432548],[-76.450577,42.434781],[-76.455577,42.434836],[-76.458759,42.435946],[-76.458898,42.438128],[-76.461577,42.438809],[-76.466099,42.442606],[-76.464577,42.444003],[-76.458577,42.443598],[-76.454577,42.44578],[-76.452594,42.448111],[-76.450577,42.448707],[-76.446577,42.45155],[-76.440577,42.452472],[-76.438577,42.454801],[-76.442577,42.454598],[-76.446577,42.452706],[-76.449577,42.452709],[-76.450577,42.45048],[-76.45896,42.449511],[-76.466132,42.452128],[-76.46527,42.455128],[-76.473577,42.457868],[-76.475924,42.463128],[-76.476577,42.466596],[-76.471577,42.466572],[-76.467379,42.46493],[-76.464225,42.46748],[-76.465716,42.469267],[-76.468577,42.469357],[-76.469577,42.471132],[-76.472577,42.46976],[-76.475128,42.470128],[-76.475922,42.472473],[-76.474547,42.475128],[-76.477756,42.477128],[-76.477876,42.480128],[-76.474577,42.481343],[-76.468331,42.481374],[-76.469151,42.478702],[-76.467634,42.475071],[-76.4651,42.474128],[-76.465577,42.480314],[-76.461577,42.482593],[-76.455577,42.482704],[-76.454577,42.481743],[-76.450052,42.482128],[-76.454577,42.484442],[-76.462577,42.483733],[-76.463577,42.482681],[-76.468577,42.483644],[-76.476577,42.483066],[-76.481577,42.481804],[-76.486278,42.482829],[-76.491577,42.480834],[-76.495577,42.481012],[-76.493917,42.483128],[-76.500868,42.483837],[-76.502166,42.487128],[-76.504577,42.48936],[-76.506515,42.489128],[-76.505577,42.484372],[-76.503577,42.48469],[-76.502328,42.481879],[-76.504875,42.481426],[-76.504915,42.478128],[-76.508832,42.481128],[-76.50985,42.484128],[-76.509782,42.488128],[-76.510887,42.489128],[-76.510926,42.493128],[-76.509491,42.493042],[-76.508864,42.498841],[-76.506173,42.500128],[-76.509066,42.502128],[-76.509133,42.505128],[-76.507936,42.508128],[-76.508577,42.514225]]]},"properties":{"fill-opacity":0.33,"opacity":0.33,"fill":"%237dcff5","fillOpacity":0.33,"color":"%23bf4040","contour":10,"metric":"time"}}]}),pin-l-home+744ce1(-76.48957711003335,42.44212800096554)/-76.4821,42.4615,10.98,0/600x400@2x?attribution=true&amp;amp;logo=true&amp;amp;before_layer=admin-0-boundary-bg&amp;amp;access_token=YOUR_MAPBOX_ACCESS_TOKEN&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generate the isochrone polygon using the &lt;a href="https://docs.mapbox.com/api/navigation/isochrone/" rel="noopener noreferrer"&gt;Mapbox Isochrone API&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add it as a GeoJSON polygon overlay with a semi-transparent fill&lt;/li&gt;
&lt;li&gt;Place a marker overlay at the property location to clearly identify the starting point&lt;/li&gt;
&lt;li&gt;Use the &lt;code&gt;before_layer&lt;/code&gt; parameter to position overlay underneath the map labels&lt;/li&gt;
&lt;li&gt;Use a dark basemap style to make the colors pop&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. School Proximity Map with Labeled Markers&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Parents searching for a new home often prioritize school proximity. For a real estate or neighborhood information app, showing a home's location alongside nearby schools provides instant context about the area.&lt;br&gt;
Using the Static Images API, you can overlay multiple custom markers — one for the home and one for each nearby school — with labeled markers (A, B, C) to make it easy to cross-reference with a list displayed alongside the map.&lt;/p&gt;

&lt;p&gt;Example:&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%2Fed31mgxfyjm6hdwqaope.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%2Fed31mgxfyjm6hdwqaope.png" alt="a static map image showing a marker for a home, and 3 markers for nearby schools labeled A, B, and C" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;https://api.mapbox.com/styles/v1/mapbox/light-v11/static/pin-s-c+4682b4(-122.3332,47.6113),pin-s-b+4682b4(-122.3401,47.6161),pin-s-a+4682b4(-122.3521,47.6138),pin-l-star+66ff2e(-122.3428,47.6139)/-122.3425,47.6141,13.8,0/600x400@2x?attribution=true&amp;amp;logo=true&amp;amp;access_token=YOUR_MAPBOX_ACCES_TOKEN&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use a distinct marker style for the home location&lt;/li&gt;
&lt;li&gt;Add labeled markers for each school with letters A, B, C (&lt;a href="https://docs.mapbox.com/api/maps/static-images/#marker" rel="noopener noreferrer"&gt;Glyphs are also available&lt;/a&gt; as marker labels using the &lt;code&gt;label&lt;/code&gt; parameter)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Use the Developer Playground to get started
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://docs.mapbox.com/playground/static/" rel="noopener noreferrer"&gt;Static Images API Playground&lt;/a&gt; makes it easy to get started without needing to learn the intricacies of the overlay DSL or manually dial your map in to your desired area and zoom level. The Playground provides a &lt;strong&gt;graphical user interface&lt;/strong&gt; where you can easily position the map, add overlays visually, and tinker with all the API parameters in real time.&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%2Fsexqdgzovi764au87kku.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%2Fsexqdgzovi764au87kku.gif" alt="a gif showing the use of the Mapbox Static Images API Playground to move the map and create a marker overlay" width="720" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We recently improved the overlay UI to make it even simpler to add markers, lines, and polygons — just click, drag, and configure. Once your map image and overlays look exactly the way you want, simply copy the generated URL.&lt;/p&gt;

&lt;p&gt;You can use that URL directly in your frontend code as an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; source, or use it as a template to generate similar URLs dynamically based on your application's data — just swap in different coordinates, zoom levels, or overlay parameters as needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Choose Static Maps
&lt;/h2&gt;

&lt;p&gt;Static maps aren't a replacement for interactive maps — they're a complement. &lt;/p&gt;

&lt;p&gt;Use static maps when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You're displaying many maps at once (list views, galleries, grids)&lt;/li&gt;
&lt;li&gt;The map is supplementary context, not the primary interaction&lt;/li&gt;
&lt;li&gt;You need fast page loads and minimal JavaScript overhead&lt;/li&gt;
&lt;li&gt;You want a consistent image that won't change based on user interaction&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By combining beautiful Mapbox basemap styles with your own overlay data, the Static Images API gives you a lightweight, performant way to bring maps into your web and mobile applications — without the complexity or cost of full interactive rendering.&lt;/p&gt;

&lt;p&gt;Ready to get started? &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Try the &lt;a href="https://docs.mapbox.com/playground/static/" rel="noopener noreferrer"&gt;Static Images API Playground&lt;/a&gt; and see how easy it is to create custom static maps for your next project. &lt;/li&gt;
&lt;li&gt;See the &lt;a href="https://docs.mapbox.com/api/maps/static-images/" rel="noopener noreferrer"&gt;API reference documentation&lt;/a&gt; for detailed breakdowns of each parameter.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>mapping</category>
      <category>gis</category>
      <category>geojson</category>
    </item>
    <item>
      <title>Gradio X Mapbox</title>
      <dc:creator>Chris Whong</dc:creator>
      <pubDate>Thu, 29 Jan 2026 16:25:53 +0000</pubDate>
      <link>https://forem.com/mapbox/gradio-x-mapbox-982</link>
      <guid>https://forem.com/mapbox/gradio-x-mapbox-982</guid>
      <description>&lt;p&gt;A developer on &lt;a href="https://www.reddit.com/r/mapbox/" rel="noopener noreferrer"&gt;r/mapbox&lt;/a&gt; asked for if anyone had successfully integrated a &lt;a href="https://docs.mapbox.com/mapbox-gl-js/guides/" rel="noopener noreferrer"&gt;Mapbox GL JS&lt;/a&gt; map with &lt;a href="https://www.gradio.app/" rel="noopener noreferrer"&gt;Gradio&lt;/a&gt;, a python library for creating web interfaces for machine learning models.&lt;/p&gt;

&lt;p&gt;After a quick peek at &lt;a href="https://www.gradio.app/guides/quickstart" rel="noopener noreferrer"&gt;Gradio's docs&lt;/a&gt;, I quickly found the sections on &lt;a href="https://www.gradio.app/guides/custom-CSS-and-JS" rel="noopener noreferrer"&gt;adding custom CSS and JS&lt;/a&gt;. With a bit of help from AI, I learned the best way to get data out of python world and into JavaScript world in a Gradio app (there are several ways, but the one I used involves stashing it in a hidden output textarea)&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%2F4r6mkkktmn5bos5moooe.webp" 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%2F4r6mkkktmn5bos5moooe.webp" alt=" " width="800" height="525"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I built a demo to show a simple implementation of Gradio X Mapbox:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start with a pandas dataframe of U.S. cities with their longitude/latitude coordinates.&lt;/li&gt;
&lt;li&gt;Convert the city points to GeoJSON in python, dropping the GeoJSON in a hidden output.&lt;/li&gt;
&lt;li&gt;On map load, pick up the GeoJSON and add circles to the map representing the cities using a &lt;code&gt;geojson&lt;/code&gt; source and a &lt;code&gt;fill&lt;/code&gt; layer.&lt;/li&gt;
&lt;li&gt;Add a button to trigger some processing in python. In this case, it's the calculation of a convex hull around the point data.&lt;/li&gt;
&lt;li&gt;Convert the convex hull to GeoJSON and stash it in another hidden output.&lt;/li&gt;
&lt;li&gt;Render the convex hull polygon on the on the map using a &lt;code&gt;geojson&lt;/code&gt; source and a &lt;code&gt;fill&lt;/code&gt; layer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are a few considerations for loading Mapbox GL JS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Load Mapbox GL JS and its css via CDN links using the &lt;code&gt;head&lt;/code&gt; argument of &lt;code&gt;demo.launch&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Create a map container div using &lt;code&gt;gr.HTML()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Before instantiating the map, poll for the existence of the map container (it may not exist when the JavaScript code first runs). In the example below, there is a timeout and subsequent checks for setting up the map.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The full code snippet is below if you want to give it a try. You'll need a Mapbox access token to get it working, so &lt;a href="https://account.mapbox.com/auth/signup/" rel="noopener noreferrer"&gt;sign up here&lt;/a&gt; if you don't already have a Mapbox account.&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;gradio&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;gr&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;pd&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;np&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nx"&gt;shapely&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;geometry&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;MultiPoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Point&lt;/span&gt;


&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;cities&lt;/span&gt; &lt;span class="nx"&gt;DataFrame&lt;/span&gt;
&lt;span class="nx"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;city&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;New York&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;Los Angeles&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;Chicago&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;Houston&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;Phoenix&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;Philadelphia&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;San Antonio&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;San Diego&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;Dallas&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;San Jose&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;lat&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="mf"&gt;40.7128&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mf"&gt;34.0522&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mf"&gt;41.8781&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mf"&gt;29.7604&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mf"&gt;33.4484&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mf"&gt;39.9526&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mf"&gt;29.4241&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mf"&gt;32.7157&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mf"&gt;32.7767&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mf"&gt;37.3382&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;lon&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;-&lt;/span&gt;&lt;span class="mf"&gt;74.0060&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;118.2437&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;87.6298&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;95.3698&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;112.0740&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;75.1652&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;98.4936&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;117.1611&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;96.7970&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;121.8863&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="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;compute&lt;/span&gt; &lt;span class="nx"&gt;convex&lt;/span&gt; &lt;span class="nx"&gt;hull&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;GeoJSON&lt;/span&gt;
&lt;span class="nx"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;compute_convex_hull&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="nx"&gt;points&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lon&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lat&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;iterrows&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
    &lt;span class="nx"&gt;multipoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MultiPoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;points&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;hull&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;multipoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;convex_hull&lt;/span&gt;
    &lt;span class="nx"&gt;hull_geojson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hull&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;features&lt;/span&gt; &lt;span class="o"&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;Feature&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;geometry&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pt&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;properties&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;city&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;city&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="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;pt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;points&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="nf"&gt;iterrows&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;geojson&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;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;FeatureCollection&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;features&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;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;Feature&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;geometry&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;hull_geojson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;properties&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;convex_hull&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="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;features&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="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;geojson&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;convert&lt;/span&gt; &lt;span class="nx"&gt;points&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;GeoJSON&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;initial&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt;
&lt;span class="nx"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_points_geojson&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="dl"&gt;"""&lt;/span&gt;&lt;span class="s2"&gt;Convert dataframe to GeoJSON for initial point display&lt;/span&gt;&lt;span class="dl"&gt;"""&lt;/span&gt;
    &lt;span class="nx"&gt;features&lt;/span&gt; &lt;span class="o"&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;Feature&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;geometry&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;Point&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;coordinates&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="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lon&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lat&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;properties&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;city&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;city&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="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;iterrows&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="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&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;FeatureCollection&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;features&lt;/span&gt;&lt;span class="dl"&gt;"&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="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;Gradio&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;two&lt;/span&gt; &lt;span class="nx"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="nx"&gt;table&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;right&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt;
&lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;gr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Blocks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;demo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;gr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Row&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;gr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="nx"&gt;gr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Markdown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="dl"&gt;"""&lt;/span&gt;&lt;span class="s2"&gt;
                # US Cities Convex Hull Demo
                This demo computes the convex hull of a set of US cities and outputs the result as GeoJSON for display on a [Mapbox GL JS](https://docs.mapbox.com/mapbox-gl-js/) map.

                GeoJSON strings are assembled in python and stored in hidden Gradio outputs for easy access in the Javascript map code.
                &lt;/span&gt;&lt;span class="dl"&gt;"""&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nx"&gt;gr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Dataframe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;df&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;City Locations&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nx"&gt;btn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;gr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Compute Convex Hull GeoJSON&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


        &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="nx"&gt;includes&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Mapbox&lt;/span&gt; &lt;span class="nx"&gt;GL&lt;/span&gt; &lt;span class="nx"&gt;JS&lt;/span&gt; &lt;span class="nx"&gt;will&lt;/span&gt; &lt;span class="nx"&gt;add&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="nx"&gt;div&lt;/span&gt;
        &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;gr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="nx"&gt;gr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;HTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="dl"&gt;"""&lt;/span&gt;&lt;span class="s2"&gt;
                &amp;lt;div&amp;gt;
                    &amp;lt;h3&amp;gt;Mapbox GL JS Visualization&amp;lt;/h3&amp;gt;
                    &amp;lt;div id='map-container' style='width: 100%; height: 450px; background-color: lightgray;'&amp;gt;&amp;lt;/div&amp;gt;
                    &amp;lt;div&amp;gt;© Mapbox &amp;amp;nbsp; © OpenStreetMap&amp;lt;/div&amp;gt;
                &amp;lt;/div&amp;gt;
                &lt;/span&gt;&lt;span class="dl"&gt;"""&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;


    &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;empty&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="nx"&gt;boxes&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;hold&lt;/span&gt; &lt;span class="nx"&gt;GeoJSON&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;
    &lt;span class="nx"&gt;geojson_output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;gr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;visible&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;initial_points&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;gr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;get_points_geojson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;df&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;visible&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


    &lt;span class="nx"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_click&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;compute_convex_hull&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;df&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


    &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;on&lt;/span&gt; &lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;compute&lt;/span&gt; &lt;span class="nx"&gt;convex&lt;/span&gt; &lt;span class="nx"&gt;hull&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="nx"&gt;update&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;
    &lt;span class="nx"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;handle_click&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;outputs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;geojson_output&lt;/span&gt;&lt;span class="p"&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;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;inputs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;geojson_output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;outputs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;js&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"""&lt;/span&gt;&lt;span class="s2"&gt;
        (data) =&amp;gt; {
            console.log(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="nx"&gt;Received&lt;/span&gt; &lt;span class="nx"&gt;GeoJSON&lt;/span&gt; &lt;span class="nx"&gt;data&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);
            if (window.map) {
                // Remove existing hull layer if it exists
                if (window.map.getLayer('convex-hull')) {
                    window.map.removeLayer('convex-hull');
                }
                if (window.map.getSource('hull')) {
                    window.map.removeSource('hull');
                }


                // Add hull
                window.map.addSource('hull', {
                    type: 'geojson',
                    data: data,
                });


                window.map.addLayer({
                    id: 'convex-hull',
                    type: 'fill',
                    source: 'hull',
                    paint: {
                        'fill-color': '#088',
                        'fill-opacity': 0.3,
                        'fill-emissive-strength': 1,
                    },
                });


                window.map.addLayer({
                    id: 'convex-hull-outline',
                    type: 'line',
                    source: 'hull',
                    paint: {
                        'line-color': '#FFF',
                        'line-width': 2,
                        'line-emissive-strength': 1,
                    },
                });
            }
        }
        &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="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;on&lt;/span&gt; &lt;span class="nx"&gt;initial&lt;/span&gt; &lt;span class="nx"&gt;load&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;initialize&lt;/span&gt; &lt;span class="nx"&gt;a&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;then&lt;/span&gt; &lt;span class="nx"&gt;add&lt;/span&gt; &lt;span class="nx"&gt;points&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;GeoJSON&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="nx"&gt;circle&lt;/span&gt; &lt;span class="nx"&gt;layer&lt;/span&gt;
    &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nf"&gt;initMap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;contains&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;retry&lt;/span&gt; &lt;span class="nx"&gt;mechanism&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;wait&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;be&lt;/span&gt; &lt;span class="nx"&gt;available&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Gradio&lt;/span&gt; &lt;span class="nx"&gt;may&lt;/span&gt; &lt;span class="nx"&gt;take&lt;/span&gt; &lt;span class="nx"&gt;some&lt;/span&gt; &lt;span class="nx"&gt;time&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;HTML&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt; &lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;may&lt;/span&gt; &lt;span class="nx"&gt;not&lt;/span&gt; &lt;span class="nx"&gt;be&lt;/span&gt; &lt;span class="nx"&gt;present&lt;/span&gt; &lt;span class="nx"&gt;immediately&lt;/span&gt;
    &lt;span class="nx"&gt;demo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;inputs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;initial_points&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;outputs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;js&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"""&lt;/span&gt;&lt;span class="s2"&gt;
        (pointsData) =&amp;gt; {
            function initMap() {
                const container = document.getElementById('map-container');
                if (!container) {
                    setTimeout(initMap, 100);
                    return;
                }


                mapboxgl.accessToken = 'YOUR_MAPBOX_ACCESS_TOKEN';


                window.map = new mapboxgl.Map({
                    container: 'map-container',
                    style: 'mapbox://styles/mapbox/standard',
                    config: {
                        basemap: {
                            theme: 'monochrome',
                            lightPreset: 'night',
                        },
                    },
                    center: [-98.92906, 40.25617],
                    zoom: 2.5,
                    projection: 'mercator',
                    attributionControl: false,
                });


                // Add points after map loads
                window.map.on('load', function () {
                    window.map.addSource('cities', {
                        type: 'geojson',
                        data: pointsData,
                    });


                    window.map.addLayer({
                        id: 'city-points',
                        type: 'circle',
                        source: 'cities',
                        paint: {
                            'circle-radius': 6,
                            'circle-color': '#B42222',
                            'circle-emissive-strength': 1,
                        },
                    });
                });
            }


            initMap();
        }
        &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="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;load&lt;/span&gt; &lt;span class="nx"&gt;mapbox&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt; &lt;span class="nx"&gt;js&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="nx"&gt;its&lt;/span&gt; &lt;span class="nx"&gt;css&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;head&lt;/span&gt;
&lt;span class="nx"&gt;head&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"""&lt;/span&gt;&lt;span class="s2"&gt;
&amp;lt;link href=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="nx"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//api.mapbox.com/mapbox-gl-js/v3.18.1/mapbox-gl.css" rel="stylesheet"&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.mapbox.com/mapbox-gl-js/v3.18.1/mapbox-gl.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="dl"&gt;"""&lt;/span&gt;&lt;span class="s2"&gt;


# custom css to position the Mapbox logo, as Gradio css interferes with default positioning
# note that attributionControl is set to false in the map options because Gradio CSS also interferes with the attribution control
# map attribution is required and was manually added in the HTML above
custom_css = &lt;/span&gt;&lt;span class="dl"&gt;"""&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mapboxgl&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;ctrl&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;bottom&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mapboxgl&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;ctrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mapboxgl&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;ctrl&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mapboxgl&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;ctrl&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="nx"&gt;px&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="nx"&gt;px&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;important&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;


demo.launch(head=head, css=custom_css)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>mapbox</category>
      <category>python</category>
      <category>javascript</category>
      <category>datascience</category>
    </item>
    <item>
      <title>Introducing Appearances: A Simpler Way to Dynamically Style Mapbox Map Icons</title>
      <dc:creator>Chris Whong</dc:creator>
      <pubDate>Fri, 28 Nov 2025 12:52:09 +0000</pubDate>
      <link>https://forem.com/mapbox/introducing-appearances-a-simpler-way-to-dynamically-style-mapbox-map-icons-3ga7</link>
      <guid>https://forem.com/mapbox/introducing-appearances-a-simpler-way-to-dynamically-style-mapbox-map-icons-3ga7</guid>
      <description>&lt;p&gt;Mapbox maps just got a major usability upgrade with the new &lt;strong&gt;appearances&lt;/strong&gt; functionality, making it easier than ever to change icon images based on user interaction—without the verbose, error-prone workarounds of the past.&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%2F5jrsn514oraslmlxeuff.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%2F5jrsn514oraslmlxeuff.gif" alt="Screen capture of an example map showing feature-state and appearances" width="790" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This screen capture shows appearances in action, displaying different icons for four distinct feature states:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;default icon&lt;/li&gt;
&lt;li&gt;feature hovered&lt;/li&gt;
&lt;li&gt;feature selected&lt;/li&gt;
&lt;li&gt;feature was previously selected&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What Are Appearances?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Appearances&lt;/strong&gt; allow you to define how a symbol layer's icons should change in response to changes in &lt;a href="https://docs.mapbox.com/mapbox-gl-js/example/hover-styles/" rel="noopener noreferrer"&gt;feature state&lt;/a&gt; (the ability to set state on specific features on a map). The &lt;a href="https://docs.mapbox.com/style-spec/reference/appearances/" rel="noopener noreferrer"&gt;&lt;code&gt;appearances&lt;/code&gt;&lt;/a&gt; layer property contains one or more appearance objects defining layout properties like &lt;a href="https://docs.mapbox.com/style-spec/reference/layers/#layout-symbol-icon-image" rel="noopener noreferrer"&gt;&lt;code&gt;icon-image&lt;/code&gt;&lt;/a&gt; and the conditions on which to apply them. For example, the following symbol layer defaults to using the image &lt;code&gt;hotel&lt;/code&gt;, but includes appearances for 3 additional icons for 3 distinct feature states (&lt;code&gt;currentlySelected&lt;/code&gt;, &lt;code&gt;hover&lt;/code&gt;, and &lt;code&gt;hasBeenClicked&lt;/code&gt;).&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="nl"&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;points&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&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;symbol&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&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;points&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;layout&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;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="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;hotel&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-size&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.75&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;appearances&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;clicked&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;condition&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;boolean&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;feature-state&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;currentlySelected&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="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="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;hotel-active&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="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="s1"&gt;hovered&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;condition&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;boolean&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;feature-state&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;hover&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="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="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;hotel-hover&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="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="s1"&gt;has-been-clicked&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;condition&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;boolean&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;feature-state&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;hasBeenClicked&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="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="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;hotel-clicked&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Appearances make the display logic for different states concise and easy to understand. The map style responds in a predictable way to feature state changes, &lt;strong&gt;so your frontend code can focus on managing state, not directly updating the map&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;To make use of the appearances defined in the symbol layer above, you just need to set feature state as the user interacts with each marker.&lt;/p&gt;

&lt;p&gt;For example, when a feature is hovered, set its &lt;code&gt;hover&lt;/code&gt; feature state to &lt;code&gt;true&lt;/code&gt; using an &lt;strong&gt;interaction&lt;/strong&gt;:&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;addInteraction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hover-in&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;mouseenter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;layerId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;points&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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;setFeatureState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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="na"&gt;hover&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="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getCanvas&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;cursor&lt;/span&gt; &lt;span class="o"&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="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;On click, the process is similar. The &lt;code&gt;currentlySelected&lt;/code&gt; feature state for the clicked feature is set to &lt;code&gt;true&lt;/code&gt;:&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;addInteraction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&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;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;layerId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;points&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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;// deselect the currently selected feature&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;selectedFeature&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;setFeatureState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;selectedFeature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;currentlySelected&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="c1"&gt;// set selectedFeature and add to list of clickedFeatures&lt;/span&gt;
        &lt;span class="nx"&gt;selectedFeature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&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;clickedFeatures&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="nx"&gt;e&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;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setFeatureState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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="na"&gt;currentlySelected&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="na"&gt;hasBeenClicked&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can &lt;a href="https://docs.mapbox.com/mapbox-gl-js/example/appearances/" rel="noopener noreferrer"&gt;see the full example outlined above&lt;/a&gt; and try it for yourself in the Mapbox docs.&lt;/p&gt;

&lt;p&gt;The above example shows frontend implementation for the web using &lt;a href="https://docs.mapbox.com/mapbox-gl-js/guides/" rel="noopener noreferrer"&gt;Mapbox GL JS&lt;/a&gt;, but the same patterns apply to the Maps SDKs for &lt;a href="https://docs.mapbox.com/ios/maps/guides/" rel="noopener noreferrer"&gt;iOS&lt;/a&gt; and &lt;a href="https://docs.mapbox.com/android/maps/guides/" rel="noopener noreferrer"&gt;Android&lt;/a&gt;. See the iOS and Android code examples linked at the bottom of this post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Zoom-dependent expressions
&lt;/h2&gt;

&lt;p&gt;In addition to using feature state to determine which icon to show, you can also use zoom expressions. The following layer definition uses appearances the &lt;code&gt;icon-size&lt;/code&gt; property based on the current zoom:&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&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="s1"&gt;points&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;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="s1"&gt;symbol&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&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;points&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;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="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="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;hotel&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-size&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;appearances&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="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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zoomed-in&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;condition&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;&amp;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;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;16&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;properties&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;icon-size&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.2&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="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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zoomed-mid&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;condition&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;&amp;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;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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;properties&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;icon-size&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.8&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;/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.amazonaws.com%2Fuploads%2Farticles%2Fct2qan9q2zqntmnyd8uk.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%2Fct2qan9q2zqntmnyd8uk.gif" alt="Screenshot showing zoom-dependent icon size with appearances" width="720" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Old Way: Updating feature properties
&lt;/h2&gt;

&lt;p&gt;Before appearances, the only way to achieve per-feature icon changes was to store the icon name in each feature's properties and update the entire GeoJSON source every time a state changed. This approach is verbose and requires a lot of client-side code to track state, update properties, and re-set the data source:&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&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="s1"&gt;points&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;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="s1"&gt;symbol&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&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;points&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;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="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="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="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;icon&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-size&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.75&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;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;setFeatureIcon&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="nx"&gt;icon&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;source&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="nf"&gt;getSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;points&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;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getSourceData&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;data&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;feature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&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;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;f&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;id&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="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="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;icon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setData&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This method is not only cumbersome, but it also &lt;strong&gt;does not work with vector tiles&lt;/strong&gt;, since you cannot update the data on a vector tile source. This limitation made dynamic icon styling impossible for symbol layers visualizing a vector source.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Appearances Are a Game Changer
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Declarative:&lt;/strong&gt; Define all your icon state logic in one place, right in the layer definition.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No data changes required:&lt;/strong&gt; No more manual property management or data source updates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Works with vector tiles:&lt;/strong&gt; Since appearances are based on feature-state, you can now have dynamic icons on vector tile sources, unlocking new possibilities for large-scale, performant maps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cleaner code:&lt;/strong&gt; Less boilerplate, fewer bugs, and easier to maintain.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try It Out and Share Feedback!
&lt;/h2&gt;

&lt;p&gt;We invite you to test out the new appearances feature in your projects to make symbol layers that respond to feature state or zoom changes.&lt;/p&gt;

&lt;p&gt;Have feedback or ideas? Let us know—your input helps us make Mapbox maps even better!&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.mapbox.com/style-spec/reference/appearances/" rel="noopener noreferrer"&gt;Appearances Reference - Mapbox Style Specification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.mapbox.com/blog/dynamic-symbol-styling-new-ways-to-configure-icon-state" rel="noopener noreferrer"&gt;Dynamic symbol styling: New ways to configure icon state&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also explore full working examples in the official documentation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.mapbox.com/mapbox-gl-js/example/appearances/" rel="noopener noreferrer"&gt;Mapbox GL JS Appearances Example&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.mapbox.com/ios/maps/examples/swiftui-appearances/" rel="noopener noreferrer"&gt;iOS Maps SDK Appearances Example (SwiftUI)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.mapbox.com/android/maps/examples/compose/appearances/" rel="noopener noreferrer"&gt;Android Maps SDK Appearances Example (Compose)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>mapbox</category>
      <category>ai</category>
    </item>
    <item>
      <title>Quickstart: Mapbox in React Native</title>
      <dc:creator>Chris Whong</dc:creator>
      <pubDate>Wed, 26 Nov 2025 17:38:22 +0000</pubDate>
      <link>https://forem.com/mapbox/quickstart-mapbox-in-react-native-2hff</link>
      <guid>https://forem.com/mapbox/quickstart-mapbox-in-react-native-2hff</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This is a condensed version of the full &lt;a href="https://docs.mapbox.com/help/tutorials/getting-started-react-native/" rel="noopener noreferrer"&gt;React Native Mapbox tutorial&lt;/a&gt;. For advanced features, troubleshooting, and more, check out the full tutorial.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://github.com/rnmapbox/maps" rel="noopener noreferrer"&gt;&lt;code&gt;@rnmapbox/maps&lt;/code&gt;&lt;/a&gt; is a community-maintained react native package that wraps the Mapbox Maps SDKs for &lt;a href="https://docs.mapbox.com/ios/maps/guides/" rel="noopener noreferrer"&gt;iOS&lt;/a&gt; and &lt;a href="https://docs.mapbox.com/android/maps/guides/" rel="noopener noreferrer"&gt;Android&lt;/a&gt;, allowing for development of cross-platform mobile mapping apps. &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%2Fmp2d7yy41yhlcu3k8hb1.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%2Fmp2d7yy41yhlcu3k8hb1.png" alt="Screenshot of an iOS emulator showing a Mapbox map centered over New York City" width="800" height="1589"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Create a New React Native Project
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @react-native-community/cli@latest init MyMapboxApp
&lt;span class="nb"&gt;cd &lt;/span&gt;MyMapboxApp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Install Mapbox Dependencies
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @rnmapbox/maps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Mapbox Access Token &amp;amp; Platform-specific Setup
&lt;/h2&gt;

&lt;p&gt;Get a token from your &lt;a href="https://account.mapbox.com/" rel="noopener noreferrer"&gt;Mapbox account&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  iOS Setup
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Podfile hooks:&lt;/strong&gt; In your &lt;code&gt;ios/Podfile&lt;/code&gt;, add the following inside your app's &lt;code&gt;target&lt;/code&gt; block:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;   &lt;span class="n"&gt;pre_install&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;installer&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
     &lt;span class="vg"&gt;$RNMapboxMaps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pre_install&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;installer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;

   &lt;span class="n"&gt;post_install&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;installer&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
     &lt;span class="vg"&gt;$RNMapboxMaps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post_install&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;installer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="c1"&gt;# ...other post_install hooks&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt; &lt;strong&gt;Install pods:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;cd &lt;/span&gt;ios &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; pod &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt; &lt;strong&gt;Add access token:&lt;/strong&gt; In &lt;code&gt;ios/{YourProjectName}/Info.plist&lt;/code&gt;, add inside &lt;code&gt;&amp;lt;dict&amp;gt;&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&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;key&amp;gt;&lt;/span&gt;MBXAccessToken&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;YOUR_MAPBOX_ACCESS_TOKEN&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Android Setup
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt; &lt;strong&gt;Add access token:&lt;/strong&gt; Create &lt;code&gt;android/app/src/main/res/values/mapbox_access_token.xml&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&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;resources&amp;gt;&lt;/span&gt;
     &lt;span class="nt"&gt;&amp;lt;string&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"mapbox_access_token"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;YOUR_MAPBOX_ACCESS_TOKEN&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;/resources&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt; &lt;strong&gt;Add Mapbox Maven repo:&lt;/strong&gt; In &lt;code&gt;android/settings.gradle&lt;/code&gt;, add:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gradle"&gt;&lt;code&gt;   &lt;span class="n"&gt;dependencyResolutionManagement&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
     &lt;span class="n"&gt;repositoriesMode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RepositoriesMode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PREFER_SETTINGS&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
     &lt;span class="k"&gt;repositories&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
       &lt;span class="n"&gt;google&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
       &lt;span class="n"&gt;mavenCentral&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
       &lt;span class="n"&gt;maven&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"https://api.mapbox.com/downloads/v2/releases/maven"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
     &lt;span class="o"&gt;}&lt;/span&gt;
   &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Update &lt;code&gt;App.tsx&lt;/code&gt; to Show a Map
&lt;/h2&gt;

&lt;p&gt;Replace the contents of &lt;code&gt;App.tsx&lt;/code&gt; with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&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="p"&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;MapView&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Camera&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;@rnmapbox/maps&lt;/span&gt;&lt;span class="dl"&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&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;&lt;/span&gt;&lt;span class="nc"&gt;MapView&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;flex&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="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Camera&lt;/span&gt;
        &lt;span class="na"&gt;defaultSettings&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;zoomLevel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;centerCoordinate&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;74.006&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;40.7128&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;// New York City&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;MapView&lt;/span&gt;&lt;span class="p"&gt;&amp;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;
  
  
  5. Run Your App
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx react-native run-ios &lt;span class="c"&gt;# or run-android&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see a Mapbox map centered on New York City. From here, you are ready to add your own data to the map, customize the style of the basemap, add interactivity, and more!&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%2Fmp2d7yy41yhlcu3k8hb1.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%2Fmp2d7yy41yhlcu3k8hb1.png" alt="Screenshot of an iOS emulator showing a Mapbox map centered over New York City" width="800" height="1589"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;For more features (GeoJSON data, basemap configuration, user location, etc.), see the &lt;a href="https://docs.mapbox.com/help/tutorials/getting-started-react-native/" rel="noopener noreferrer"&gt;full tutorial&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>mobile</category>
      <category>reactnative</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Adding a Hover Tooltip to a Polygon Layer in Mapbox GL JS</title>
      <dc:creator>Chris Whong</dc:creator>
      <pubDate>Thu, 20 Nov 2025 20:00:27 +0000</pubDate>
      <link>https://forem.com/mapbox/adding-a-hover-tooltip-to-a-polygon-layer-in-mapbox-gl-js-2d6n</link>
      <guid>https://forem.com/mapbox/adding-a-hover-tooltip-to-a-polygon-layer-in-mapbox-gl-js-2d6n</guid>
      <description>&lt;p&gt;Hover tooltips are a great way to enhance the UX of your web maps. When the user moves their mouse across a region, district, delivery zone, or any other polygon, the map can talk back — instantly showing what the user is pointing at.&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%2Fnyw02ydpx1m6wntmksk6.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%2Fnyw02ydpx1m6wntmksk6.gif" alt="Screen capture of hover tooltips on a polygon layer in a web map" width="648" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At Mapbox, we just published an &lt;a href="https://docs.mapbox.com/mapbox-gl-js/example/hover-tooltip/" rel="noopener noreferrer"&gt;updated code example&lt;/a&gt; demonstrating this pattern, and it highlights one of the most common UX needs: &lt;strong&gt;a lightweight tooltip that follows the cursor and displays the properties of the polygon underneath.&lt;/strong&gt; If you're designing thematic maps, dashboards, or interactive exploratory tools, this is a pattern worth adding to your toolkit.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Why this pattern matters&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A hover-follow tooltip gives users:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Instant context without clicking
&lt;/li&gt;
&lt;li&gt;A smooth, responsive feel as users move the mouse across boundaries
&lt;/li&gt;
&lt;li&gt;A clean, focused UI (no permanent labels cluttering the map)
&lt;/li&gt;
&lt;li&gt;A natural interaction pattern for data-rich polygons
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach uses a single popup that moves dynamically with the cursor, updating its content with the feature you're hovering.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Key Concepts (with snippet examples)&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;1. Create a single reusable popup&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;You don’t want a new popup on every movement — just one that updates.&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;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;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;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;offset&lt;/span&gt;&lt;span class="p"&gt;:&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="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;// keeps tooltip above the cursor&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Update the popup position + content on &lt;code&gt;mousemove&lt;/code&gt;
The new example uses map.addInteraction to target a specific polygon layer and update the tooltip as the cursor moves across it.
&lt;/li&gt;
&lt;/ol&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;addInteraction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hover-tooltip&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;mousemove&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;layerId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;polygon-fills&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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="nx"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&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;name&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="nf"&gt;setLngLat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lngLat&lt;/span&gt;&lt;span class="p"&gt;)&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="s2"&gt;`&amp;lt;strong&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/strong&amp;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;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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This keeps the tooltip synced to the cursor while pulling feature properties on demand.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Hide the tooltip on &lt;code&gt;mouseleave&lt;/code&gt;
A tiny interaction detail that keeps your UI clean:
&lt;/li&gt;
&lt;/ol&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;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;mouseleave&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;polygon-fills&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;popup&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  See the full working example
&lt;/h2&gt;

&lt;p&gt;These snippets illustrate the pattern, but the full example includes a real dataset, layer styling, and the complete interaction flow.&lt;/p&gt;

&lt;h3&gt;
  
  
  👉 Check out the latest Mapbox GL JS hover tooltip example:
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.mapbox.com/mapbox-gl-js/example/hover-tooltip/" rel="noopener noreferrer"&gt;https://docs.mapbox.com/mapbox-gl-js/example/hover-tooltip/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The example shows exactly how this pattern works in a realistic scenario — a polygon dataset that updates the tooltip in real time as you move the mouse pointer across the map.&lt;/p&gt;

&lt;h2&gt;
  
  
  Explore More UX Patterns
&lt;/h2&gt;

&lt;p&gt;If you're building interactive maps—dashboards, analytics tools, administrative boundaries, or selection-based interfaces—you’ll find more great patterns across the Mapbox GL JS example gallery.&lt;/p&gt;

&lt;h3&gt;
  
  
  👉 Browse all Mapbox GL JS examples
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.mapbox.com/mapbox-gl-js/examples/" rel="noopener noreferrer"&gt;https://docs.mapbox.com/mapbox-gl-js/examples/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These examples are excellent starting points for designing modern, intuitive map interactions to make your web maps sing!&lt;/p&gt;

</description>
      <category>mapbox</category>
      <category>webdev</category>
      <category>mapping</category>
      <category>ux</category>
    </item>
    <item>
      <title>Mapbox Developer Tutorials help you start building quickly</title>
      <dc:creator>Chris Whong</dc:creator>
      <pubDate>Wed, 19 Nov 2025 16:25:17 +0000</pubDate>
      <link>https://forem.com/mapbox/mapbox-developer-tutorials-help-you-start-building-quickly-46j4</link>
      <guid>https://forem.com/mapbox/mapbox-developer-tutorials-help-you-start-building-quickly-46j4</guid>
      <description>&lt;p&gt;At &lt;a href="https://www.mapbox.com" rel="noopener noreferrer"&gt;Mapbox&lt;/a&gt;, we’ve been shipping &lt;em&gt;a ton&lt;/em&gt; of new developer tutorials to help you get started and level up as you integrate our maps and location services into your web and mobile apps.&lt;/p&gt;

&lt;p&gt;But first:&lt;br&gt;&lt;br&gt;
🔥 &lt;strong&gt;We launched a brand-new step-by-step tutorial UI.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
It’s cleaner, more focused, and way easier to follow as you move through code, concepts, and hands-on tasks. &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%2Fcb7raijwixa5y82q8i0t.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%2Fcb7raijwixa5y82q8i0t.gif" alt="Screen Capture of New Mapbox Tutorial UI" width="600" height="362"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Below is the full list of new and refreshed tutorials — grouped by platform so you can jump right into the stack you’re working with.&lt;/p&gt;




&lt;h2&gt;
  
  
  🕸️ Web Tutorials
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;React, Angular, Vue &amp;amp; GL JS&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Dynamic Markers &amp;amp; Popups in React&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://docs.mapbox.com/help/tutorials/dynamic-markers-react/" rel="noopener noreferrer"&gt;https://docs.mapbox.com/help/tutorials/dynamic-markers-react/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;POI Search in React (Search Box API)&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://docs.mapbox.com/help/tutorials/poi-search-react/" rel="noopener noreferrer"&gt;https://docs.mapbox.com/help/tutorials/poi-search-react/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Toggle Layers with Checkboxes (React)&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://docs.mapbox.com/help/tutorials/react-toggle-layers/" rel="noopener noreferrer"&gt;https://docs.mapbox.com/help/tutorials/react-toggle-layers/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Store Locator with React + GL JS&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://docs.mapbox.com/help/tutorials/building-a-store-locator-react/" rel="noopener noreferrer"&gt;https://docs.mapbox.com/help/tutorials/building-a-store-locator-react/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Store Locator with Vanilla JavaScript&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://docs.mapbox.com/help/tutorials/building-a-store-locator/" rel="noopener noreferrer"&gt;https://docs.mapbox.com/help/tutorials/building-a-store-locator/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;GL JS in an Angular App&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://docs.mapbox.com/help/tutorials/use-mapbox-gl-js-with-angular/" rel="noopener noreferrer"&gt;https://docs.mapbox.com/help/tutorials/use-mapbox-gl-js-with-angular/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;GL JS in a Vue App (rewrite)&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://docs.mapbox.com/help/tutorials/use-mapbox-gl-js-with-vue/" rel="noopener noreferrer"&gt;https://docs.mapbox.com/help/tutorials/use-mapbox-gl-js-with-vue/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📱 Mobile Tutorials
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;iOS&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Turn-by-Turn Navigation&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://docs.mapbox.com/help/tutorials/ios-navigation/" rel="noopener noreferrer"&gt;https://docs.mapbox.com/help/tutorials/ios-navigation/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Geofencing&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://docs.mapbox.com/help/tutorials/ios-geofencing/" rel="noopener noreferrer"&gt;https://docs.mapbox.com/help/tutorials/ios-geofencing/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Marker App from a Custom Style + Tileset&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://docs.mapbox.com/help/tutorials/ios-marker-app-custom-style/?step=3" rel="noopener noreferrer"&gt;https://docs.mapbox.com/help/tutorials/ios-marker-app-custom-style/?step=3&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Interactions&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://docs.mapbox.com/help/tutorials/ios-interactions/" rel="noopener noreferrer"&gt;https://docs.mapbox.com/help/tutorials/ios-interactions/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Location Search (Search SDK)&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://docs.mapbox.com/help/tutorials/ios-location-search/" rel="noopener noreferrer"&gt;https://docs.mapbox.com/help/tutorials/ios-location-search/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Offline Maps (iOS)&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://docs.mapbox.com/help/tutorials/ios-offline-maps/" rel="noopener noreferrer"&gt;https://docs.mapbox.com/help/tutorials/ios-offline-maps/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Android&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Geofencing&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://docs.mapbox.com/help/tutorials/android-geofencing/" rel="noopener noreferrer"&gt;https://docs.mapbox.com/help/tutorials/android-geofencing/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Marker App from GeoJSON&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://docs.mapbox.com/help/tutorials/android-markers-from-geojson/" rel="noopener noreferrer"&gt;https://docs.mapbox.com/help/tutorials/android-markers-from-geojson/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Interactions&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://docs.mapbox.com/help/tutorials/android-interactions/" rel="noopener noreferrer"&gt;https://docs.mapbox.com/help/tutorials/android-interactions/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Location Search (Search SDK)&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://docs.mapbox.com/help/tutorials/android-location-search/" rel="noopener noreferrer"&gt;https://docs.mapbox.com/help/tutorials/android-location-search/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Offline Maps (Android)&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://docs.mapbox.com/help/tutorials/android-offline-maps/" rel="noopener noreferrer"&gt;https://docs.mapbox.com/help/tutorials/android-offline-maps/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🗺️ Mapbox Studio Tutorials
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Create a Custom Map Style (rewrite)&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://docs.mapbox.com/help/tutorials/create-a-custom-style/" rel="noopener noreferrer"&gt;https://docs.mapbox.com/help/tutorials/create-a-custom-style/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Add &amp;amp; Style Data in Studio&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://docs.mapbox.com/help/tutorials/add-data-to-mapbox-style" rel="noopener noreferrer"&gt;https://docs.mapbox.com/help/tutorials/add-data-to-mapbox-style&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧱 Data Processing &amp;amp; MTS (Mapbox Tiling Service)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cluster Point Data with MTS&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://docs.mapbox.com/help/tutorials/cluster-point-data-with-mts/" rel="noopener noreferrer"&gt;https://docs.mapbox.com/help/tutorials/cluster-point-data-with-mts/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Incremental Update with MTS&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://docs.mapbox.com/help/tutorials/mts-incremental-updates/" rel="noopener noreferrer"&gt;https://docs.mapbox.com/help/tutorials/mts-incremental-updates/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🤖 Build AI Agents with Mapbox
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://docs.mapbox.com/api/guides/mcp-server/" rel="noopener noreferrer"&gt;Mapbox MCP Server&lt;/a&gt; provides helpful skills for LLM to work with powered by Mapbox web services APIs. For example, you can retrieve directions, generate isochrones, or create static maps from your AI agent app.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Build Your Own Agent with the Mapbox MCP Server&lt;/strong&gt;
&lt;a href="https://docs.mapbox.com/help/tutorials/build-your-own-agent/" rel="noopener noreferrer"&gt;https://docs.mapbox.com/help/tutorials/build-your-own-agent/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  💬 What Should We Build Next?
&lt;/h2&gt;

&lt;p&gt;We want to know: &lt;strong&gt;What’s missing?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Is there a tutorial you wish existed? A concept that felt tricky the first time you built with Mapbox? Something you'd love to see broken down step-by-step?&lt;/p&gt;

&lt;p&gt;Drop your ideas in the comments — I read every one, and your feedback shapes what we create next.&lt;/p&gt;

&lt;p&gt;Let’s keep making location tech fun, powerful, and developer-friendly.  &lt;/p&gt;

</description>
      <category>mapbox</category>
      <category>mapping</category>
      <category>mobile</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
