[{"data":1,"prerenderedAt":706},["ShallowReactive",2],{"footer-learn-links":3,"site-prefooter-cta":16,"blog-\u002Fblog\u002Fhow-to-build-a-custom-map-style-in-30-minutes\u002F":24,"related-blog-\u002Fblog\u002Fhow-to-build-a-custom-map-style-in-30-minutes\u002F":652,"blog-author-\u002Fblog\u002Fhow-to-build-a-custom-map-style-in-30-minutes\u002F":696},[4,7,10,13],{"title":5,"path":6},"What Is a Distance Matrix API?","\u002Flearn\u002Fdistance-matrix-api",{"title":8,"path":9},"Can You Store Geocoding Results?","\u002Flearn\u002Fgeocoding-data-storage",{"title":11,"path":12},"What Does a \"Free\" Map API Actually Cost?","\u002Flearn\u002Fwhat-does-a-free-map-api-cost",{"title":14,"path":15},"What Is the Best Geocoding API?","\u002Flearn\u002Fbest-geocoding-api",{"id":17,"extension":18,"heading":19,"meta":20,"stem":21,"text":22,"__hash__":23},"site\u002Fsite\u002Fprefooter-cta.yml","yml","Business Outcomes That Fuel Your Growth",{},"site\u002Fprefooter-cta","With Stadia Maps, you build the solutions that matter. Logistics platforms provide accurate ETAs, fleet management apps reduce fuel costs, and emergency dispatch systems improve response times.","XztWiZDFyAC0XCoeYLLQrcyaVmP8uvGWALRS354c9I4",{"id":25,"title":26,"abstract":27,"author":28,"body":29,"description":630,"extension":631,"head":27,"image":632,"imageAlt":633,"keywords":634,"meta":642,"modified":27,"navigation":143,"path":643,"proficiencyLevel":27,"published":644,"rawbody":645,"schemaOrg":27,"schemaType":27,"section":646,"seo":647,"stem":650,"__hash__":651},"blog\u002Fblog\u002Fhow-to-build-a-custom-map-style-in-30-minutes.md","How to Build a Custom Map Style in 30 Minutes",null,"rob-yoegel",{"type":30,"value":31,"toc":616},"minimark",[32,39,42,45,57,62,71,79,83,86,94,97,109,112,119,263,274,281,284,288,296,315,327,343,346,374,382,400,403,407,410,416,422,490,495,516,520,525,528,532,535,539,542,546,549,553,588,592,600,613],[33,34,35],"blockquote",{},[36,37,38],"p",{},"This walkthrough forks a Stadia Maps style, recolors the background, water, road, and label layers in Maputnik, exports the JSON, and hosts it as a static file. It covers domain-based authentication versus API keys for production, and answers some of the failures developers hit most: blank maps caused by a 401 response; road layers that only partly change color because roads are split across layers by class; and palettes that break at higher zoom levels.",[36,40,41],{},"Your app has a design system, but your map doesn't match it.",[36,43,44],{},"Maybe the roads are the wrong shade of gray, the labels compete with your UI, or the entire basemap is louder than the data you're trying to put on top of it. You don't need a different map provider. You just need it to look like it belongs in your app.",[36,46,47,48,56],{},"A ",[49,50,55],"a",{"href":51,"rel":52,"target":54},"https:\u002F\u002Fmaplibre.org\u002Fmaplibre-style-spec\u002F",[53],"external","_blank","MapLibre style"," is just a JSON file, so customizing a map shouldn't be a design problem. It should be a file you edit, review, and deploy like any other asset in your repo.",[58,59,61],"h2",{"id":60},"what-youll-end-up-with","What You'll End Up With",[36,63,64,65,70],{},"In about 30 minutes, you'll have an interactive map that uses your full brand palette: your background color, your road hierarchy, and your label treatment. A single JSON file you host yourself, loaded by a few lines of ",[49,66,69],{"href":67,"rel":68,"target":54},"https:\u002F\u002Fmaplibre.org\u002Fmaplibre-gl-js\u002Fdocs\u002F",[53],"MapLibre GL JS",". The end result will look something like this:",[36,72,73],{},[74,75],"img",{"alt":76,"src":77,"style":78},"Stock Alidade Smooth and the same map with a custom brand palette applied, side by side","\u002Fimages\u002Fcontent\u002Fcustom-map-style-before-after.png","display: block; margin-inline: auto; max-width: 100%; width: auto;",[58,80,82],{"id":81},"start-with-a-working-map","Start With a Working Map",[36,84,85],{},"To get started, all you need is a browser, a Stadia Maps account, and somewhere to serve a static file.",[36,87,88,89,93],{},"Open our ",[49,90,92],{"href":91},"\u002Fbuild-a-map\u002F","Build a Map"," tool (which doesn't require your Stadia Maps login). It's a style picker, not a style editor. The color changes happen in the next step.",[36,95,96],{},"Use the tool to display a working map with your preferred map style and generate the code to load it. Styles include Alidade Bright, Alidade Smooth, Alidade Smooth Dark, Alidade Satellite, Outdoors, OSM Bright, Stamen Terrain, Stamen Toner, Stamen Toner Lite, Stamen Toner Dark, Stamen Toner Blacklite, and Stamen Watercolor.",[36,98,99,100,108],{},"If you don't know which style to choose, start with ",[49,101,104],{"href":102,"rel":103,"target":54},"https:\u002F\u002Fdocs.stadiamaps.com\u002Fmap-styles\u002Falidade-smooth\u002F",[53],[105,106,107],"strong",{},"Alidade Smooth",", which is designed for maps with a lot of markers or overlays. Its muted palette leaves you room to work. Forking a finished style also beats starting from an empty one, because you inherit years of cartographic decisions about what to show at which zoom level.",[36,110,111],{},"Set your latitude, longitude, and zoom to frame a place you know well. You'll be judging color changes against it, so pick a spot where you'll notice when the roads look wrong.",[36,113,114,115,118],{},"Next, click the ",[105,116,117],{},"Try it in JSFiddle"," button. You now have a working interactive map. The generated code looks like this (London is used in this example):",[120,121,126],"pre",{"className":122,"code":123,"language":124,"meta":125,"style":125},"language-javascript shiki shiki-themes github-light","\u002F\u002F This is a quick sample to get you started with an interactive map.\n\n\u002F\u002F 1. Set up the map control.\nvar map = new maplibregl.Map({\n    container: 'map',\n    style: 'https:\u002F\u002Ftiles.stadiamaps.com\u002Fstyles\u002Falidade_smooth.json',  \u002F\u002F 2. Style URL; see our https:\u002F\u002Fdocs.stadiamaps.com\u002Fthemes\u002F for more options\n    \u002F\u002F You can also explicitly request tiles from our EU servers using the following URL:\n    \u002F\u002F https:\u002F\u002Ftiles-eu.stadiamaps.com\u002Fstyles\u002Falidade_smooth.json\n    center: { lat: 51.5072, lng: -0.1276 },  \u002F\u002F 3. Initial focus coordinate\n    zoom: 7,  \u002F\u002F 4. Initial zoom\n});\n","javascript","",[127,128,129,138,145,151,178,191,206,212,218,243,257],"code",{"__ignoreMap":125},[130,131,134],"span",{"class":132,"line":133},"line",1,[130,135,137],{"class":136},"sAwPA","\u002F\u002F This is a quick sample to get you started with an interactive map.\n",[130,139,141],{"class":132,"line":140},2,[130,142,144],{"emptyLinePlaceholder":143},true,"\n",[130,146,148],{"class":132,"line":147},3,[130,149,150],{"class":136},"\u002F\u002F 1. Set up the map control.\n",[130,152,154,158,162,165,168,171,175],{"class":132,"line":153},4,[130,155,157],{"class":156},"sD7c4","var",[130,159,161],{"class":160},"sgsFI"," map ",[130,163,164],{"class":156},"=",[130,166,167],{"class":156}," new",[130,169,170],{"class":160}," maplibregl.",[130,172,174],{"class":173},"s7eDp","Map",[130,176,177],{"class":160},"({\n",[130,179,181,184,188],{"class":132,"line":180},5,[130,182,183],{"class":160},"    container: ",[130,185,187],{"class":186},"sYBdl","'map'",[130,189,190],{"class":160},",\n",[130,192,194,197,200,203],{"class":132,"line":193},6,[130,195,196],{"class":160},"    style: ",[130,198,199],{"class":186},"'https:\u002F\u002Ftiles.stadiamaps.com\u002Fstyles\u002Falidade_smooth.json'",[130,201,202],{"class":160},",  ",[130,204,205],{"class":136},"\u002F\u002F 2. Style URL; see our https:\u002F\u002Fdocs.stadiamaps.com\u002Fthemes\u002F for more options\n",[130,207,209],{"class":132,"line":208},7,[130,210,211],{"class":136},"    \u002F\u002F You can also explicitly request tiles from our EU servers using the following URL:\n",[130,213,215],{"class":132,"line":214},8,[130,216,217],{"class":136},"    \u002F\u002F https:\u002F\u002Ftiles-eu.stadiamaps.com\u002Fstyles\u002Falidade_smooth.json\n",[130,219,221,224,228,231,234,237,240],{"class":132,"line":220},9,[130,222,223],{"class":160},"    center: { lat: ",[130,225,227],{"class":226},"sYu0t","51.5072",[130,229,230],{"class":160},", lng: ",[130,232,233],{"class":156},"-",[130,235,236],{"class":226},"0.1276",[130,238,239],{"class":160}," },  ",[130,241,242],{"class":136},"\u002F\u002F 3. Initial focus coordinate\n",[130,244,246,249,252,254],{"class":132,"line":245},10,[130,247,248],{"class":160},"    zoom: ",[130,250,251],{"class":226},"7",[130,253,202],{"class":160},[130,255,256],{"class":136},"\u002F\u002F 4. Initial zoom\n",[130,258,260],{"class":132,"line":259},11,[130,261,262],{"class":160},"});\n",[36,264,265,266,269,270,273],{},"The HTML pane loads MapLibre GL JS and its stylesheet, and the JavaScript pane adds a ",[127,267,268],{},"setRTLTextPlugin"," call, so right-to-left labels render correctly. Keep this tab open. That ",[127,271,272],{},"style"," line is the only one you'll change at the end.",[36,275,276,277,280],{},"If your users are in the EU and you want tiles served from EU infrastructure, the code notes the alternative endpoint: ",[127,278,279],{},"https:\u002F\u002Ftiles-eu.stadiamaps.com\u002Fstyles\u002Falidade_smooth.json",".",[36,282,283],{},"You have a map, and you know which line controls its appearance. Next, you'll change what that line points to.",[58,285,287],{"id":286},"customize-the-style-in-maputnik","Customize the Style in Maputnik",[36,289,290,295],{},[49,291,294],{"href":292,"rel":293,"target":54},"https:\u002F\u002Fmaputnik.github.io\u002Feditor\u002F",[53],"Maputnik"," runs in the browser. It's free and open source, and it edits the same JSON your map already loads.",[36,297,298,301,302,305,306,309,310,280],{},[105,299,300],{},"Open your starting style."," In the Maputnik menu bar, choose ",[105,303,304],{},"Open",", then enter the JSON URL for the style you picked. For Alidade Smooth, that's ",[127,307,308],{},"https:\u002F\u002Ftiles.stadiamaps.com\u002Fstyles\u002Falidade_smooth.json",". The full list lives in our ",[49,311,314],{"href":312,"rel":313,"target":54},"https:\u002F\u002Fdocs.stadiamaps.com\u002Fthemes\u002F",[53],"style library documentation",[36,316,317,320,321,326],{},[105,318,319],{},"Find the layers that matter."," A production basemap has many ",[49,322,325],{"href":323,"rel":324,"target":54},"https:\u002F\u002Fdocs.stadiamaps.com\u002Fvector\u002F",[53],"layers",", but these are the most important to focus on:",[328,329,330,334,337,340],"ul",{},[331,332,333],"li",{},"Background and landcover, basically the base color everything sits on",[331,335,336],{},"Water, usually the strongest color contrast on the map",[331,338,339],{},"Roads, split across several layers by class, which is why changing \"the road color\" means changing more than one thing",[331,341,342],{},"Labels, including text color, halo color, and font",[36,344,345],{},"Use Maputnik's layer filter to search by name instead of scrolling. Change the background first and let it set the range everything else has to work within.",[36,347,348,351,352,357,358,363,364,367,368,373],{},[105,349,350],{},"Match your palette and check your contrast."," Paste in your brand hex values, then zoom from country level to street level. Colors that look right at ",[49,353,356],{"href":354,"rel":355,"target":54},"https:\u002F\u002Fwiki.openstreetmap.org\u002Fwiki\u002FZoom_levels",[53],"zoom 7"," often fall apart at zoom 15, when ",[49,359,362],{"href":360,"rel":361,"target":54},"https:\u002F\u002Fmaplibre.org\u002Fmaplibre-style-spec\u002Flayers\u002F",[53],"more layers switch on"," at their ",[127,365,366],{},"minzoom"," thresholds. Check the label text against the ",[49,369,372],{"href":370,"rel":371,"target":54},"https:\u002F\u002Fwww.w3.org\u002FWAI\u002FWCAG22\u002FUnderstanding\u002Fcontrast-minimum.html",[53],"WCAG minimum of 4.5:1"," while you're here, since a palette that passes on a white page can fail over landcover fills. Most of the time, what you spend here determines whether a map looks branded or broken.",[36,375,376,377,381],{},"Check the ",[49,378,380],{"href":379},"\u002Fattribution\u002F","attribution requirements"," for the style you pick. Keep in mind that Stamen styles carry their own license terms for commercial use and remixing. Also, Stamen Watercolor is a raster style, so it isn't vector-styleable.",[36,383,384,385,388,389,394,395,280],{},"Click ",[105,386,387],{},"Export"," in the top menu and save the JSON. If you'll be loading tiles with an ",[49,390,393],{"href":391,"rel":392,"target":54},"https:\u002F\u002Fdocs.stadiamaps.com\u002Fauthentication\u002F",[53],"API key",", which covers mobile apps, server-side rendering, and anything outside a browser, enter your API key in the export dialog, and ",[49,396,399],{"href":397,"rel":398,"target":54},"https:\u002F\u002Fdocs.stadiamaps.com\u002Fcustom-styles\u002F",[53],"Maputnik writes it into the style for you",[36,401,402],{},"You now have your style as a file. All that's left is putting it somewhere your app can reach.",[58,404,406],{"id":405},"host-your-style-and-ship-it","Host Your Style and Ship It",[36,408,409],{},"Some mapping platforms keep your style on their servers, so this step never comes up. We hand you the file instead. Your style is a portable artifact you own: you can diff it, review it in a pull request, roll it back, bundle it offline, and take it with you. It's a design asset in your repository, not a row in someone else's database.",[36,411,412,415],{},[105,413,414],{},"Host the exported JSON"," anywhere you serve static files: your own server, a CDN, or bundled inside your app.",[36,417,418,421],{},[105,419,420],{},"Point your map at it."," Go back to that JSFiddle tab and change one line:",[120,423,425],{"className":122,"code":424,"language":124,"meta":125,"style":125},"var map = new maplibregl.Map({\n    container: 'map',\n    style: 'https:\u002F\u002Fyourdomain.com\u002Fstyles\u002Fyour-custom-style.json',  \u002F\u002F your style now\n    center: { lat: 51.5072, lng: -0.1276 },\n    zoom: 7,\n});\n",[127,426,427,443,451,463,478,486],{"__ignoreMap":125},[130,428,429,431,433,435,437,439,441],{"class":132,"line":133},[130,430,157],{"class":156},[130,432,161],{"class":160},[130,434,164],{"class":156},[130,436,167],{"class":156},[130,438,170],{"class":160},[130,440,174],{"class":173},[130,442,177],{"class":160},[130,444,445,447,449],{"class":132,"line":140},[130,446,183],{"class":160},[130,448,187],{"class":186},[130,450,190],{"class":160},[130,452,453,455,458,460],{"class":132,"line":147},[130,454,196],{"class":160},[130,456,457],{"class":186},"'https:\u002F\u002Fyourdomain.com\u002Fstyles\u002Fyour-custom-style.json'",[130,459,202],{"class":160},[130,461,462],{"class":136},"\u002F\u002F your style now\n",[130,464,465,467,469,471,473,475],{"class":132,"line":153},[130,466,223],{"class":160},[130,468,227],{"class":226},[130,470,230],{"class":160},[130,472,233],{"class":156},[130,474,236],{"class":226},[130,476,477],{"class":160}," },\n",[130,479,480,482,484],{"class":132,"line":180},[130,481,248],{"class":160},[130,483,251],{"class":226},[130,485,190],{"class":160},[130,487,488],{"class":132,"line":193},[130,489,262],{"class":160},[36,491,492],{},[105,493,494],{},"Reload.",[36,496,497,500,501,506,507,511,512,515],{},[105,498,499],{},"Set up authentication before you go live."," For production websites, register your domain in the ",[49,502,505],{"href":503,"rel":504,"target":54},"https:\u002F\u002Fclient.stadiamaps.com\u002Fdashboard\u002F",[53],"Stadia Maps dashboard"," and use ",[49,508,510],{"href":391,"rel":509,"target":54},[53],"domain-based authentication"," to keep API keys out of your client code. For mobile apps, server-side code, and intranet sites, use an API key. Local development on ",[127,513,514],{},"localhost"," requires no authentication, which is why your fiddle worked immediately.",[58,517,519],{"id":518},"troubleshooting-and-common-questions","Troubleshooting and Common Questions",[521,522,524],"h3",{"id":523},"why-is-my-map-blank-after-switching-to-my-custom-style","Why is my map blank after switching to my custom style?",[36,526,527],{},"Check the browser console. A blank map with a 401 response is an authentication issue. Either your domain isn't registered yet, or your API key didn't make it into the style. A blank map with no network errors usually indicates a malformed export or an invalid style URL.",[521,529,531],{"id":530},"why-didnt-some-layers-change-color","Why didn't some layers change color?",[36,533,534],{},"Roads and labels are split across multiple layers by class and zoom level, so changing one road layer changes only one road class. Filter the layer list by name and check for siblings.",[521,536,538],{"id":537},"why-do-my-colors-look-right-at-low-zoom-but-wrong-at-high-zoom-levels","Why do my colors look right at low zoom but wrong at high zoom levels?",[36,540,541],{},"Layers switch on as you zoom in, and some properties use zoom-dependent expressions rather than fixed values. Test every color change across the full zoom range before committing to it.",[521,543,545],{"id":544},"do-i-have-to-re-export-when-the-base-style-is-updated","Do I have to re-export when the base style is updated?",[36,547,548],{},"Your exported style is a snapshot. It keeps working as-is, but it won't automatically pick up upstream improvements. When an update ships that you want, re-fork and reapply your changes, which is much faster if your palette is documented in your repository.",[521,550,552],{"id":551},"can-i-use-custom-icons-and-fonts","Can I use custom icons and fonts?",[36,554,555,556,561,562,565,566,571,572,575,576,581,582,587],{},"Yes, though both take more setup than anything else here. Generate sprite files with ",[49,557,560],{"href":558,"rel":559,"target":54},"https:\u002F\u002Fgithub.com\u002Fflother\u002Fspreet",[53],"Spreet",", host them yourself, and point your style's ",[127,563,564],{},"sprite"," key at them. ",[49,567,570],{"href":568,"rel":569,"target":54},"https:\u002F\u002Fdocs.stadiamaps.com\u002Fcustom-styles\u002F#advanced-customizing-sprites",[53],"Our docs cover the details",", including why the URL leaves off the file extension. For fonts, generate glyphs with our own ",[127,573,574],{},"build_pbf_glyphs"," or ",[49,577,580],{"href":578,"rel":579,"target":54},"https:\u002F\u002Fmaplibre.org\u002Ffont-maker\u002F",[53],"MapLibre Font Maker","; ",[49,583,586],{"href":584,"rel":585,"target":54},"https:\u002F\u002Fdocs.stadiamaps.com\u002Fcustom-styles\u002F#advanced-customizing-fonts",[53],"the font docs"," explain how to combine scripts into a single stack with fallbacks. Check the license on anything you ship.",[58,589,591],{"id":590},"take-the-next-step","Take the Next Step",[36,593,594,595,599],{},"You now have a branded basemap and a repeatable process for changing it. From here, the natural next steps are adding your own data layers on top, swapping in custom marker sprites, or building a dark variant from the same palette. Our ",[49,596,598],{"href":397,"rel":597,"target":54},[53],"custom styling documentation"," goes deeper on sprites and glyphs when you're ready.",[36,601,602,603,608,609],{},"Ready to ",[49,604,607],{"href":605,"rel":606,"target":54},"https:\u002F\u002Fclient.stadiamaps.com\u002Fsignup\u002F",[53],"get started with Stadia Maps for free","? Every new account includes a 14-day trial of Stadia Maps Professional, and we don't require a credit card when you sign up. Need help? ",[49,610,612],{"href":611},"\u002Fsupport\u002F","Reach out to our Support team at any time.",[272,614,615],{},"html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .sD7c4, html code.shiki .sD7c4{--shiki-default:#D73A49}html pre.shiki code .sgsFI, html code.shiki .sgsFI{--shiki-default:#24292E}html pre.shiki code .s7eDp, html code.shiki .s7eDp{--shiki-default:#6F42C1}html pre.shiki code .sYBdl, html code.shiki .sYBdl{--shiki-default:#032F62}html pre.shiki code .sYu0t, html code.shiki .sYu0t{--shiki-default:#005CC5}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":125,"searchDepth":153,"depth":153,"links":617},[618,619,620,621,622,629],{"id":60,"depth":140,"text":61},{"id":81,"depth":140,"text":82},{"id":286,"depth":140,"text":287},{"id":405,"depth":140,"text":406},{"id":518,"depth":140,"text":519,"children":623},[624,625,626,627,628],{"id":523,"depth":147,"text":524},{"id":530,"depth":147,"text":531},{"id":537,"depth":147,"text":538},{"id":544,"depth":147,"text":545},{"id":551,"depth":147,"text":552},{"id":590,"depth":140,"text":591},"Fork a Stadia Maps style, recolor it in Maputnik, and host the JSON yourself. A step-by-step walkthrough for making a basemap match your app's design system.","md","\u002Fimages\u002Fog\u002Fhow-to-build-a-custom-map-style-in-30-minutes.png","How to Build a Custom Map Style in 30 Minutes — Stadia Maps",[635,636,294,637,638,107,639,640,510,641],"custom map style","MapLibre style spec","map branding","basemap customization","vector tiles","style JSON hosting","map color palette",{},"\u002Fblog\u002Fhow-to-build-a-custom-map-style-in-30-minutes","2026-10-01","---\ntitle: How to Build a Custom Map Style in 30 Minutes\ndescription: Fork a Stadia Maps style, recolor it in Maputnik, and host the JSON yourself. A step-by-step walkthrough for making a basemap match your app's design system.\nauthor: rob-yoegel\nimage: \u002Fimages\u002Fog\u002Fhow-to-build-a-custom-map-style-in-30-minutes.png\nimageAlt: How to Build a Custom Map Style in 30 Minutes — Stadia Maps\nkeywords:\n  - custom map style\n  - MapLibre style spec\n  - Maputnik\n  - map branding\n  - basemap customization\n  - Alidade Smooth\n  - vector tiles\n  - style JSON hosting\n  - domain-based authentication\n  - map color palette\npublished: 2026-10-01\nsection: Maps\nseo:\n  title: Build a Custom Map Style in 30 Minutes\n  ogTitle: How to Build a Custom Map Style in 30 Minutes\n  description: Fork a MapLibre style, recolor it in Maputnik, export the JSON, and host it yourself. Includes authentication setup and answers to common styling problems.\n---\n\n> This walkthrough forks a Stadia Maps style, recolors the background, water, road, and label layers in Maputnik, exports the JSON, and hosts it as a static file. It covers domain-based authentication versus API keys for production, and answers some of the failures developers hit most: blank maps caused by a 401 response; road layers that only partly change color because roads are split across layers by class; and palettes that break at higher zoom levels.\n\nYour app has a design system, but your map doesn't match it.\n\nMaybe the roads are the wrong shade of gray, the labels compete with your UI, or the entire basemap is louder than the data you're trying to put on top of it. You don't need a different map provider. You just need it to look like it belongs in your app.\n\nA [MapLibre style](https:\u002F\u002Fmaplibre.org\u002Fmaplibre-style-spec\u002F) is just a JSON file, so customizing a map shouldn't be a design problem. It should be a file you edit, review, and deploy like any other asset in your repo.\n\n## What You'll End Up With\n\nIn about 30 minutes, you'll have an interactive map that uses your full brand palette: your background color, your road hierarchy, and your label treatment. A single JSON file you host yourself, loaded by a few lines of [MapLibre GL JS](https:\u002F\u002Fmaplibre.org\u002Fmaplibre-gl-js\u002Fdocs\u002F). The end result will look something like this:\n\n![Stock Alidade Smooth and the same map with a custom brand palette applied, side by side](\u002Fimages\u002Fcontent\u002Fcustom-map-style-before-after.png){style=\"display: block; margin-inline: auto; max-width: 100%; width: auto;\"}\n\n## Start With a Working Map\n\nTo get started, all you need is a browser, a Stadia Maps account, and somewhere to serve a static file.\n\nOpen our [Build a Map](\u002Fbuild-a-map\u002F) tool (which doesn't require your Stadia Maps login). It's a style picker, not a style editor. The color changes happen in the next step.\n\nUse the tool to display a working map with your preferred map style and generate the code to load it. Styles include Alidade Bright, Alidade Smooth, Alidade Smooth Dark, Alidade Satellite, Outdoors, OSM Bright, Stamen Terrain, Stamen Toner, Stamen Toner Lite, Stamen Toner Dark, Stamen Toner Blacklite, and Stamen Watercolor.\n\nIf you don't know which style to choose, start with [**Alidade Smooth**](https:\u002F\u002Fdocs.stadiamaps.com\u002Fmap-styles\u002Falidade-smooth\u002F), which is designed for maps with a lot of markers or overlays. Its muted palette leaves you room to work. Forking a finished style also beats starting from an empty one, because you inherit years of cartographic decisions about what to show at which zoom level.\n\nSet your latitude, longitude, and zoom to frame a place you know well. You'll be judging color changes against it, so pick a spot where you'll notice when the roads look wrong.\n\nNext, click the **Try it in JSFiddle** button. You now have a working interactive map. The generated code looks like this (London is used in this example):\n\n```javascript\n\u002F\u002F This is a quick sample to get you started with an interactive map.\n\n\u002F\u002F 1. Set up the map control.\nvar map = new maplibregl.Map({\n    container: 'map',\n    style: 'https:\u002F\u002Ftiles.stadiamaps.com\u002Fstyles\u002Falidade_smooth.json',  \u002F\u002F 2. Style URL; see our https:\u002F\u002Fdocs.stadiamaps.com\u002Fthemes\u002F for more options\n    \u002F\u002F You can also explicitly request tiles from our EU servers using the following URL:\n    \u002F\u002F https:\u002F\u002Ftiles-eu.stadiamaps.com\u002Fstyles\u002Falidade_smooth.json\n    center: { lat: 51.5072, lng: -0.1276 },  \u002F\u002F 3. Initial focus coordinate\n    zoom: 7,  \u002F\u002F 4. Initial zoom\n});\n```\n\nThe HTML pane loads MapLibre GL JS and its stylesheet, and the JavaScript pane adds a `setRTLTextPlugin` call, so right-to-left labels render correctly. Keep this tab open. That `style` line is the only one you'll change at the end.\n\nIf your users are in the EU and you want tiles served from EU infrastructure, the code notes the alternative endpoint: `https:\u002F\u002Ftiles-eu.stadiamaps.com\u002Fstyles\u002Falidade_smooth.json`.\n\nYou have a map, and you know which line controls its appearance. Next, you'll change what that line points to.\n\n## Customize the Style in Maputnik\n\n[Maputnik](https:\u002F\u002Fmaputnik.github.io\u002Feditor\u002F) runs in the browser. It's free and open source, and it edits the same JSON your map already loads.\n\n**Open your starting style.** In the Maputnik menu bar, choose **Open**, then enter the JSON URL for the style you picked. For Alidade Smooth, that's `https:\u002F\u002Ftiles.stadiamaps.com\u002Fstyles\u002Falidade_smooth.json`. The full list lives in our [style library documentation](https:\u002F\u002Fdocs.stadiamaps.com\u002Fthemes\u002F).\n\n**Find the layers that matter.** A production basemap has many [layers](https:\u002F\u002Fdocs.stadiamaps.com\u002Fvector\u002F), but these are the most important to focus on:\n\n- Background and landcover, basically the base color everything sits on\n- Water, usually the strongest color contrast on the map\n- Roads, split across several layers by class, which is why changing \"the road color\" means changing more than one thing\n- Labels, including text color, halo color, and font\n\nUse Maputnik's layer filter to search by name instead of scrolling. Change the background first and let it set the range everything else has to work within.\n\n**Match your palette and check your contrast.** Paste in your brand hex values, then zoom from country level to street level. Colors that look right at [zoom 7](https:\u002F\u002Fwiki.openstreetmap.org\u002Fwiki\u002FZoom_levels) often fall apart at zoom 15, when [more layers switch on](https:\u002F\u002Fmaplibre.org\u002Fmaplibre-style-spec\u002Flayers\u002F) at their `minzoom` thresholds. Check the label text against the [WCAG minimum of 4.5:1](https:\u002F\u002Fwww.w3.org\u002FWAI\u002FWCAG22\u002FUnderstanding\u002Fcontrast-minimum.html) while you're here, since a palette that passes on a white page can fail over landcover fills. Most of the time, what you spend here determines whether a map looks branded or broken.\n\nCheck the [attribution requirements](\u002Fattribution\u002F) for the style you pick. Keep in mind that Stamen styles carry their own license terms for commercial use and remixing. Also, Stamen Watercolor is a raster style, so it isn't vector-styleable.\n\nClick **Export** in the top menu and save the JSON. If you'll be loading tiles with an [API key](https:\u002F\u002Fdocs.stadiamaps.com\u002Fauthentication\u002F), which covers mobile apps, server-side rendering, and anything outside a browser, enter your API key in the export dialog, and [Maputnik writes it into the style for you](https:\u002F\u002Fdocs.stadiamaps.com\u002Fcustom-styles\u002F).\n\nYou now have your style as a file. All that's left is putting it somewhere your app can reach.\n\n## Host Your Style and Ship It\n\nSome mapping platforms keep your style on their servers, so this step never comes up. We hand you the file instead. Your style is a portable artifact you own: you can diff it, review it in a pull request, roll it back, bundle it offline, and take it with you. It's a design asset in your repository, not a row in someone else's database.\n\n**Host the exported JSON** anywhere you serve static files: your own server, a CDN, or bundled inside your app.\n\n**Point your map at it.** Go back to that JSFiddle tab and change one line:\n\n```javascript\nvar map = new maplibregl.Map({\n    container: 'map',\n    style: 'https:\u002F\u002Fyourdomain.com\u002Fstyles\u002Fyour-custom-style.json',  \u002F\u002F your style now\n    center: { lat: 51.5072, lng: -0.1276 },\n    zoom: 7,\n});\n```\n\n**Reload.**\n\n**Set up authentication before you go live.** For production websites, register your domain in the [Stadia Maps dashboard](https:\u002F\u002Fclient.stadiamaps.com\u002Fdashboard\u002F) and use [domain-based authentication](https:\u002F\u002Fdocs.stadiamaps.com\u002Fauthentication\u002F) to keep API keys out of your client code. For mobile apps, server-side code, and intranet sites, use an API key. Local development on `localhost` requires no authentication, which is why your fiddle worked immediately.\n\n## Troubleshooting and Common Questions\n\n### Why is my map blank after switching to my custom style?\n\nCheck the browser console. A blank map with a 401 response is an authentication issue. Either your domain isn't registered yet, or your API key didn't make it into the style. A blank map with no network errors usually indicates a malformed export or an invalid style URL.\n\n### Why didn't some layers change color?\n\nRoads and labels are split across multiple layers by class and zoom level, so changing one road layer changes only one road class. Filter the layer list by name and check for siblings.\n\n### Why do my colors look right at low zoom but wrong at high zoom levels?\n\nLayers switch on as you zoom in, and some properties use zoom-dependent expressions rather than fixed values. Test every color change across the full zoom range before committing to it.\n\n### Do I have to re-export when the base style is updated?\n\nYour exported style is a snapshot. It keeps working as-is, but it won't automatically pick up upstream improvements. When an update ships that you want, re-fork and reapply your changes, which is much faster if your palette is documented in your repository.\n\n### Can I use custom icons and fonts?\n\nYes, though both take more setup than anything else here. Generate sprite files with [Spreet](https:\u002F\u002Fgithub.com\u002Fflother\u002Fspreet), host them yourself, and point your style's `sprite` key at them. [Our docs cover the details](https:\u002F\u002Fdocs.stadiamaps.com\u002Fcustom-styles\u002F#advanced-customizing-sprites), including why the URL leaves off the file extension. For fonts, generate glyphs with our own `build_pbf_glyphs` or [MapLibre Font Maker](https:\u002F\u002Fmaplibre.org\u002Ffont-maker\u002F); [the font docs](https:\u002F\u002Fdocs.stadiamaps.com\u002Fcustom-styles\u002F#advanced-customizing-fonts) explain how to combine scripts into a single stack with fallbacks. Check the license on anything you ship.\n\n## Take the Next Step\n\nYou now have a branded basemap and a repeatable process for changing it. From here, the natural next steps are adding your own data layers on top, swapping in custom marker sprites, or building a dark variant from the same palette. Our [custom styling documentation](https:\u002F\u002Fdocs.stadiamaps.com\u002Fcustom-styles\u002F) goes deeper on sprites and glyphs when you're ready.\n\nReady to [get started with Stadia Maps for free](https:\u002F\u002Fclient.stadiamaps.com\u002Fsignup\u002F)? Every new account includes a 14-day trial of Stadia Maps Professional, and we don't require a credit card when you sign up. Need help? [Reach out to our Support team at any time.](\u002Fsupport\u002F)\n","Maps",{"title":648,"ogTitle":26,"description":649},"Build a Custom Map Style in 30 Minutes","Fork a MapLibre style, recolor it in Maputnik, export the JSON, and host it yourself. Includes authentication setup and answers to common styling problems.","blog\u002Fhow-to-build-a-custom-map-style-in-30-minutes","utFXsn3k_Xe7srVl5ki-BZERUtpD4wwkfsjsOYa9R5g",[653,669,684],{"title":654,"description":655,"path":656,"published":657,"keywords":658,"rawbody":668},"Revisiting the Cartography Behind Stamen Terrain","Our recent water layer and water label changes reshaped how Stamen Terrain renders coastlines, fjords, and lakes. Four before-and-after comparisons show the difference.","\u002Fblog\u002Frevisiting-the-cartography-behind-stamen-terrain","2026-09-14",[659,660,661,639,662,663,664,665,666,667],"Stamen Terrain","map cartography","water rendering","coastline generalization","zoom level discontinuities","OpenStreetMap water data","Natural Earth","tile size","basemap styles","---\ntitle: Revisiting the Cartography Behind Stamen Terrain\ndescription: Our recent water layer and water label changes reshaped how Stamen Terrain renders coastlines, fjords, and lakes. Four before-and-after comparisons show the difference.\nauthor: ian-wagner\nimage: \u002Fimages\u002Fog\u002Frevisiting-the-cartography-behind-stamen-terrain.png\nimageAlt: Revisiting the Cartography Behind Stamen Terrain — Stadia Maps\nkeywords:\n  - Stamen Terrain\n  - map cartography\n  - water rendering\n  - vector tiles\n  - coastline generalization\n  - zoom level discontinuities\n  - OpenStreetMap water data\n  - Natural Earth\n  - tile size\n  - basemap styles\npublished: 2026-09-14\nsection: Maps\nseo:\n  title: \"Stamen Terrain: Improved Water Rendering\"\n  ogTitle: Revisiting the Cartography Behind Stamen Terrain\n  description: How processing OSM water data at every zoom level removed coastline jumps in Stamen Terrain, preserved more detail, and shrank low-zoom vector tiles.\n---\n\n> We've been taking a fresh look at the cartographic decisions at every level of our map stack. Our recent [water layer changes](\u002Fblog\u002Ffixing-missing-water-interactive-basemaps\u002F) and [water label updates](\u002Fblog\u002Fwater-labels-reworked-cleaner-names-smarter-placement\u002F) made a visible difference to the Stamen Terrain style.\n\nRendering water at low- to mid-zoom levels trades detail against tile size. We want to represent the important features without bloating the tiles with irrelevant detail.\n\nFor polygonal features like water bodies, there already exist several generalized, pre-processed global datasets at varying levels of detail. We used these in the past, but found these created visual jumps when going from one zoom level to the next.\n\nThis was sometimes because of incompatible generalization strategies. For example, many low-resolution water datasets intentionally leave out minor features like fjords, which are not strictly important for a general large-area map but are still visible from a great distance. This is fine for their intended use case, but problematic for ours.\n\nThe other broad class of problem we noticed was that switching datasets (e.g., between Natural Earth and OSM) caused larger discontinuities, as the datasets were produced independently and vary at different speeds. We addressed this by processing OSM data for every zoom level. This took some more work to process, but was absolutely worth it!\n\nFor line data, the generalization criteria are different. We, of course, want to simplify the overall shape using similar techniques to those for polygons, but \"importance\" is a bit more difficult to measure. We revisited the thresholds for inclusion at every zoom level.\n\nThe differences in [**Stamen Terrain**](\u002Fexplore-the-map\u002F#style=stamen_terrain&map=10.05\u002F47.3495\u002F9.3513) were striking enough that we've put together this screenshot tour.\n\n## What's Changed\n\nBelow are four side-by-side comparisons of the former style and the new revised version. We used Iceland, Norway, a wider view of Scandinavia, and Lake Ontario as examples. Drag the slider to compare each pair directly.\n\n**Iceland**\n\n:content-twenty-twenty{after-src=\"\u002Fimages\u002Fcontent\u002Ficeland-after.png\" before-src=\"\u002Fimages\u002Fcontent\u002Ficeland-before.png\"}\n\n**Norway**\n\n:content-twenty-twenty{after-src=\"\u002Fimages\u002Fcontent\u002Fnorway-after.png\" before-src=\"\u002Fimages\u002Fcontent\u002Fnorway-before.png\"}\n\n**Scandinavia**\n\n:content-twenty-twenty{after-src=\"\u002Fimages\u002Fcontent\u002Fscandinavia-after.png\" before-src=\"\u002Fimages\u002Fcontent\u002Fscandinavia-before.png\"}\n\n**Lake Ontario**\n\n:content-twenty-twenty{after-src=\"\u002Fimages\u002Fcontent\u002Flake-ontario-after.png\" before-src=\"\u002Fimages\u002Fcontent\u002Flake-ontario-before.png\"}\n\nAcross all examples, we've been able to preserve more detail. And, while it's harder to highlight the difference here, we've removed essentially all discontinuous coastline and waterway jumps between zoom levels.\n\nSomewhat counterintuitively, this update actually reduces vector tile size at lower zoom levels. Even though we increased the level of visual detail on the map, we got much smarter about merging contiguous bodies of water. And of course, we were also able to drop insignificant features more reliably and improve our approach to geometric simplification, both of which contributed to the reduced tile sizes.\n\nIf you look closely, you might also notice some differences in water labels between the before and after images. [You can read more about our recent overhaul of water name labeling here.](\u002Fblog\u002Fwater-labels-reworked-cleaner-names-smarter-placement\u002F)\n\n## What This Means for Your Application\n\nIf you're using [Stamen Terrain](\u002Fproducts\u002Fmaps\u002Fmap-styles\u002F), whether vector or raster, via the endpoints on our [Stamen Terrain style page](https:\u002F\u002Fdocs.stadiamaps.com\u002Fmap-styles\u002Fstamen-terrain\u002F), you're automatically getting the improved tiles. No changes needed on your end.\n\n- Water bodies and coastlines render more consistently across zoom levels, with less of the visual noise that comes from switching data sources mid-zoom\n- Lower-zoom tiles are smaller, despite showing more visual detail\n- Improved water feature labeling: maps now show significant bays, lakes, and gulfs at lower zoom levels; show labels for straits (these were not present before); and have cleaner labels for major oceans and seas\n\nFollow us for more updates as we continue updating our cartography. And if you spot anything that looks off, let us know at \u003Csupport@stadiamaps.com>.\n",{"title":670,"description":671,"path":672,"published":673,"keywords":674,"rawbody":683},"Water Labels Reworked: Cleaner Names, Smarter Placement, and Straits at Last","We overhauled water name labeling across our basemaps with better relevance in the tiles, cleaner names for seas and oceans, centered lake labels, and straits.","\u002Fblog\u002Fwater-labels-reworked-cleaner-names-smarter-placement","2026-08-24",[675,660,676,639,677,678,679,680,681,682],"water labels","water_name layer","label placement","symbol-placement line-center","OpenStreetMap water names","straits","map label language","basemap styling","---\ntitle: \"Water Labels Reworked: Cleaner Names, Smarter Placement, and Straits at Last\"\ndescription: We overhauled water name labeling across our basemaps with better relevance in the tiles, cleaner names for seas and oceans, centered lake labels, and straits.\nauthor: ian-wagner\nimage: \u002Fimages\u002Fog\u002Fwater-labels-reworked.png\nimageAlt: \"Water Labels Reworked: Cleaner Names, Smarter Placement, and Straits at Last — Stadia Maps\"\nkeywords:\n  - water labels\n  - map cartography\n  - water_name layer\n  - vector tiles\n  - label placement\n  - symbol-placement line-center\n  - OpenStreetMap water names\n  - straits\n  - map label language\n  - basemap styling\npublished: 2026-08-24\nsection: Maps\nseo:\n  title: Water Label Improvements in Basemaps\n  ogTitle: Water Labels Reworked\n  description: Better relevance selection, cleaner names for major water bodies, centered lake labels, and straits are now in the water_name layer. Here's what changed and what to check in custom styles.\n---\n\n> Around 71% of the Earth's surface is covered in water. It's not something everyone thinks about when they look at their maps, but bodies of water also provide important locational context. We're in the process of reworking our cartography from the ground up. Here are some details about our recent overhaul of water name labeling.\n\n## What We Changed\n\nThe way we approached water labeling hasn't changed much since we launched around a decade ago. I had been keeping a laundry list of things to improve for a while. It started with several variations of \"X should (or should not!) be visible at zoom Y,\" and as I dug deeper, I found a few other things we could bundle into this release.\n\n### 1. Improved relevance selection in our tiles\n\nIf you can see it from space, but it's not visible till z8, that's not great! That was, unfortunately, the story of some of the world's largest lakes, including the Great Lakes and Lake Baikal.\n\n:content-twenty-twenty{after-src=\"\u002Fimages\u002Fcontent\u002Fgreat-lakes-after.png\" before-src=\"\u002Fimages\u002Fcontent\u002Fgreat-lakes-before.png\"}\n\nThe main fix here was considering a better set of factors when determining relevance. This is baked into our tile build process, so styles don't need to make any changes.\n\nWhile you may (correctly) guess that this includes more features in the low-detail tiles, we saw an unexpected improvement in tile sizes through the mid to high detail levels. We previously included many irrelevant features at mid-zoom levels. These caused frequent collisions between lower relevance labels and higher relevance ones. Removing these reduced the overall tile size and resulted in more relevant labels showing up.\n\n### 2. Cleaner labels for major water bodies\n\nWe cleaned up labels for large maritime areas and inland seas.\n\nOne of our guiding cartographic principles is to display labels in the local language by default wherever possible. In most of the world, signage is only displayed in a single language\u002Fscript, so our maps default to showing that. We may also display a \"Latin script\" name for some features to improve international readability if the place has a name in another language, like English or French.\n\nInternational waters are tricky, though, since there is no \"local language\" and they aren't part of any one country. In OpenStreetMap, these are often given a `name` tag with half a dozen languages, separated by… something. This is not always consistent, makes the map harder to read, and crowds out other labels that could provide relevant context. In short, it's not great for anyone.\n\nWe've simplified our default styling to just the common English name for these features. Smaller bodies of water like lakes and smaller inland seas will continue to be labeled as-is, with both the local name and optionally a Latinized name.\n\nThis does not change anything about the data that we ship in the vector tiles. We still include dozens of languages, and if you're localizing a map for an audience with different language preferences, you can still [customize the styles to fit your desired label language](https:\u002F\u002Fdocs.stadiamaps.com\u002Ftutorials\u002Fchanging-the-map-label-language\u002F).\n\n:content-twenty-twenty{after-src=\"\u002Fimages\u002Fcontent\u002Fcaspian-after.png\" before-src=\"\u002Fimages\u002Fcontent\u002Fcaspian-before.png\"}\n\nBesides being cleaner, this also lets us place more labels. Note how the Black Sea was conspicuously absent before. This was because the label would have clashed with Sevastopol, so it got dropped from the middle zoom levels.\n\n### 3. Improved the placement of lake centerline labels\n\nOur in-house map styles labeled lake centerlines inconsistently. They were often off-center at lower zoom levels, and the spacing wasn't consistent.\n\nThis is now fixed, and often results in more lake labels being placeable at medium zoom levels, since moving the label to the center steers clear of settlements bordering the lake. The main fix was changing the `symbol-placement` [property](https:\u002F\u002Fmaplibre.org\u002Fmaplibre-style-spec\u002Flayers\u002F#symbol-placement) from `line` to `line-center` in all of our styles. (The brilliant cartographers over at [Stamen](\u002Fstamen\u002F) actually got this right on the first try, though, so their styles didn't suffer from this issue!)\n\n:content-twenty-twenty{after-src=\"\u002Fimages\u002Fcontent\u002Flake-centerlines-after.png\" before-src=\"\u002Fimages\u002Fcontent\u002Flake-centerlines-before.png\"}\n\n### 4. Straits are here!\n\nThat we never included these in our basemaps was a historical accident. Important straits will appear as `LineString` features in the `water_name` layer, so you get beautiful labels like these.\n\n![Stadia Maps Water Label Update - Straits](\u002Fimages\u002Fcontent\u002Fstraits.png)\n\n## What This Means for You\n\nMost Stadia Maps customers don't need to do anything. If you're using our raster map tiles or a vector style hosted at [tiles.stadiamaps.com](https:\u002F\u002Ftiles.stadiamaps.com), you already have the latest and greatest styling. All the improvements mentioned above are already live.\n\nIf you customized your own style, you may need a few minor tweaks to polish things up. The area around the English Channel highlights the two types of features most likely to require styling adjustments.\n\n![The area around the English Channel highlights the two types of features most likely to require styling adjustments.](\u002Fimages\u002Fcontent\u002Fenglish-channel.png){style=\"display: block; margin-inline: auto; max-width: 100%; width: auto;\"}\n\nIf you don't see the English Channel or the Bay of Biscay, check your style for references to the `water_name` layer and make sure that your class filters include `strait` and `bay` at lower zoom levels (we generally recommend removing zoom-based filters, unless you are intentionally creating a minimalist style). And if your layer for line-based water names doesn't do so already, set the `symbol-placement` to `line-center` to get the same visual improvements we showed above.\n",{"title":685,"description":686,"path":687,"published":688,"keywords":689,"rawbody":695},"How We Fixed Water Feature Rendering Across All Zoom Levels in Our Basemaps","Mixing Natural Earth and OSM data across zoom levels broke rivers, coastlines, and islands. Here's how a single Overture Maps dataset fixed it.","\u002Fblog\u002Ffixing-missing-water-interactive-basemaps","2026-06-04",[646,690,691,692,693,694],"Interactive Basemaps","OpenStreetMap","Overture Maps","Cartography","Vector Tiles","---\ntitle: How We Fixed Water Feature Rendering Across All Zoom Levels in Our Basemaps\ndescription: Mixing Natural Earth and OSM data across zoom levels broke rivers, coastlines, and islands. Here's how a single Overture Maps dataset fixed it.\nauthor: ian-wagner\nimage: \u002Fimages\u002Fog\u002Fwater-basemap-og.png\nimageAlt: How We Fixed Water Feature Rendering Across All Zoom Levels in Our Basemaps — Stadia Maps\nkeywords:\n  - Maps\n  - Interactive Basemaps\n  - OpenStreetMap\n  - Overture Maps\n  - Cartography\n  - Vector Tiles\npublished: 2026-06-04\nsection: Maps\nseo:\n  title: How We Fixed Water Feature Rendering on Our Basemaps\n  ogTitle: How We Fixed Water Feature Rendering Across All Zoom Levels in Our Basemaps\n  ogDescription: Mixing Natural Earth and OSM data across zoom levels broke rivers, coastlines, and islands. Here's how a single Overture Maps dataset fixed it.\n  description: Mixing Natural Earth and OSM data across zoom levels broke rivers, coastlines, and islands. Here's how a single Overture Maps dataset fixed it.\n---\n\n> We just shipped a significant update to the data pipeline behind our [Interactive Basemaps](\u002Fproducts\u002Fmaps\u002Finteractive-maps\u002F). The change fixes a class of visual inconsistencies at low- and mid-zoom levels, such as missing rivers, shifting coastlines, islands that suddenly appear, and major water features that pop into detail abruptly rather than transitioning smoothly. The root cause was mixing two different data sources at different zoom levels. The fix is a unified OSM-derived dataset across all zoom levels, sourced from [Overture Maps](https:\u002F\u002Foverturemaps.org\u002F).\n\nHere's what was happening and what we changed.\n\n## Why Water Features Were Disappearing at Mid-Zoom Levels\n\nOur basemaps were pulling from two different datasets, depending on the zoom level. At lower zooms, we used Natural Earth, a well-known dataset that efficiently represents the most important details for broad-scale cartography. At higher zooms, we switched to OSM-derived data. These two datasets don't agree on everything: coastlines have vastly different levels of detail and features that exist in one don't always have equivalents in the other at the same zoom thresholds.\n\nThe handoff point between them was the source of most of the visual inconsistencies. A customer noticed a clear example: The St. Lawrence River disappeared entirely between zooms 6 and 8. OSM represents it as a `water` polygon, while the way we processed Natural Earth's simplified data at those zoom levels didn't carry the equivalent coverage. The result is a river-shaped hole filled with land. But that was one symptom of a broader pattern. Coastlines were shifting shape mid-zoom, small islands were appearing or vanishing, and water features were popping in suddenly rather than transitioning smoothly.\n\n:content-twenty-twenty{after-src=\"\u002Fassets\u002Ficeland-new.png\" before-src=\"\u002Fassets\u002Ficeland-old.png\"}\n\n## What Changed: A Single Data Source Across All Zoom Levels\n\nWe've unified our basemaps around a single OSM-derived dataset across all zoom levels. Specifically, we're using releases from [Overture Maps](https:\u002F\u002Foverturemaps.org\u002F), which apply additional QA on top of OSM to ensure major water bodies are complete and consistent, something we had occasionally struggled with when building directly from raw OSM data.\n\nUsing the same source throughout eliminates the cross-dataset seam. No more coastline jumps. No more islands appearing from nowhere. No more rivers losing their shape at mid-zoom. It also means features that were simply absent at lower zoom levels. Rivers, estuaries, and reservoirs are now present and correct from the moment they're geographically relevant, rather than popping in abruptly as you zoom past the old data handoff point.\n\nWe also updated our geometry simplification algorithm and parameters. The new approach carries more detail through mid-zoom levels and produces smoother transitions as you zoom in and out with less of the sudden snap from one level of detail to the next.\n\nThe St. Lawrence is back where it belongs: [see it here](\u002Fexplore-the-map\u002F#map=8.86\u002F44.5347\u002F-75.4982).\n\n## What This Means for Your Application\n\nIf your application renders maps at mid-zoom levels, your users are now seeing more accurate, more consistent water rendering. Specifically:\n\n- **Rivers, estuaries, and reservoirs** now appear at the zoom levels where they're geographically relevant\n- **Coastlines** have smoother transitions, with more detail present at low and mid zoom levels\n- **Islands and water bodies** render consistently from zoom to zoom\n- **Transitions between zoom levels** are smoother across all water features\n\nThese are the kinds of artifacts that are easy to miss in development and easy to notice in production. If you ever spot anything on the map that still looks off, let us know at \u003Csupport@stadiamaps.com>. Reports like these are how this fix started.\n",{"id":697,"bio":698,"extension":18,"jobTitle":699,"meta":700,"name":701,"sameAs":702,"slug":28,"stem":704,"twitterCreator":27,"type":27,"url":27,"__hash__":705},"authors\u002Fauthors\u002Frob-yoegel.yml","Rob Yoegel is Head of Growth at Stadia Maps. He was previously Senior Director of Marketing at Linode, and Director, Global Cloud and Product-Led Growth Marketing, at Akamai.","Head of Growth",{},"Rob Yoegel",[703],"https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Frobyoegel\u002F","authors\u002Frob-yoegel","jUeXphw8YMp7NfonVi1LMfmfv9YTfSwXjQI3xVjXeuk",1790828716984]