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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

</description>
      <category>frontend</category>
      <category>opensource</category>
      <category>mapbox</category>
      <category>geospatial</category>
    </item>
    <item>
      <title>Unlocking Mapbox GL JS's Hidden Field of View Control</title>
      <dc:creator>Andrew Sepic</dc:creator>
      <pubDate>Tue, 10 Feb 2026 14:49:30 +0000</pubDate>
      <link>https://dev.to/mapbox/unlocking-mapbox-gl-jss-hidden-field-of-view-control-4h29</link>
      <guid>https://dev.to/mapbox/unlocking-mapbox-gl-jss-hidden-field-of-view-control-4h29</guid>
      <description>&lt;p&gt;Ever wished you could make your Mapbox map look more like a telephoto lens or a dramatic wide-angle shot? Turns out, there's an undocumented way to control the camera's field of view (FOV) in &lt;a href="https://docs.mapbox.com/mapbox-gl-js/" rel="noopener noreferrer"&gt;Mapbox GL JS&lt;/a&gt;—but with a big caveat: &lt;strong&gt;this is not a public API&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%2Fgllthmll1207hr4tsbos.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%2Fgllthmll1207hr4tsbos.jpeg" alt="Field of View A).64 radians B) 1.00 radians C) 1.5 radians"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Developer Warning&lt;/strong&gt;: What I'm about to show you involves accessing private internal properties. Use this only for experiments, prototypes, and creative projects. Don't rely on this in production—it could break in any future Mapbox version without notice.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: Fixed Field of View
&lt;/h2&gt;

&lt;p&gt;By default, Mapbox GL JS uses a fixed vertical field of view of approximately &lt;strong&gt;36.87 degrees&lt;/strong&gt; (0.643 radians). This gives maps a consistent, neutral perspective. But what if you want to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a telephoto effect (narrow FOV) for architectural visualization&lt;/li&gt;
&lt;li&gt;Achieve a dramatic wide-angle view (large FOV) for immersive experiences&lt;/li&gt;
&lt;li&gt;Dynamically adjust the visual perspective based on user interaction&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The official Mapbox API doesn't expose FOV control—but the internal &lt;code&gt;Transform&lt;/code&gt; class does.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the Transform Class?
&lt;/h2&gt;

&lt;p&gt;Digging through the &lt;a href="https://github.com/mapbox/mapbox-gl-js/blob/main/src/geo/transform.ts" rel="noopener noreferrer"&gt;Mapbox GL JS source code&lt;/a&gt;, I discovered the &lt;code&gt;Transform&lt;/code&gt; class—the mathematical heart of Mapbox's rendering system. This 2,800+ line beast handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Camera positioning and orientation&lt;/li&gt;
&lt;li&gt;Projection matrix calculations&lt;/li&gt;
&lt;li&gt;Coordinate system transformations (lat/lng ↔ screen pixels ↔ mercator)&lt;/li&gt;
&lt;li&gt;Zoom level calculations&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Field of view management&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;Transform&lt;/code&gt; instance lives at &lt;code&gt;map.transform&lt;/code&gt; and contains a private &lt;code&gt;_fov&lt;/code&gt; property that controls the vertical field of view.&lt;/p&gt;

&lt;h2&gt;
  
  
  The (Unofficial) Solution
&lt;/h2&gt;

&lt;p&gt;Here's how you can control field of view with a custom function called &lt;code&gt;setFOV&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setFOV&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fov&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Directly access the internal Transform instance&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;transform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_fov&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fov&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Force recalculation of projection matrices&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;transform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_calcMatrices&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// Trigger a map repaint&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;triggerRepaint&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Initialize your 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="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;71.06288&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;42.36834&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;16.85&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;pitch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;75.67&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;bearing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;141.60&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;// Apply a custom FOV (in radians)&lt;/span&gt;
  &lt;span class="nf"&gt;setFOV&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="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Wide angle&lt;/span&gt;
  &lt;span class="c1"&gt;// setFOV(map, 0.4); // Telephoto&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  FOV Values Explained
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Radians&lt;/th&gt;
&lt;th&gt;FOV effect&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;0.1 - 0.5 radians&lt;/strong&gt; (~5-28°): Telephoto effect, compressed perspective&lt;/td&gt;
&lt;td&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%2Fifbhky1vfb6us4cy64mv.png" alt=" "&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;0.643 radians&lt;/strong&gt; (~36.87°): Default Mapbox FOV&lt;/td&gt;
&lt;td&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%2Fxna7elnecedggscfn6ty.png" alt=" "&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;0.8 - 1.5 radians&lt;/strong&gt; (~45-86°): Wide angle, dramatic perspective&lt;/td&gt;
&lt;td&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%2Fe91k9gotg7etay0sc3lu.png" alt=" "&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  How It Works Under the Hood
&lt;/h2&gt;

&lt;p&gt;When you modify &lt;code&gt;_fov&lt;/code&gt;, three things need to happen:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Update the property&lt;/strong&gt;: &lt;code&gt;map.transform._fov = newValue&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recalculate matrices&lt;/strong&gt;: &lt;code&gt;map.transform._calcMatrices()&lt;/code&gt; recomputes all the projection math including:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;projMatrix&lt;/code&gt; - world to clip coordinates&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pixelMatrix&lt;/code&gt; - world to screen pixels&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cameraToCenterDistance&lt;/code&gt; - camera distance based on FOV&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trigger repaint&lt;/strong&gt;: &lt;code&gt;map.triggerRepaint()&lt;/code&gt; forces a redraw with new matrices&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;code&gt;_calcMatrices()&lt;/code&gt; method is where the magic happens. From the source:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cameraToCenterDistance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_fov&lt;/span&gt; &lt;span class="o"&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="o"&gt;*&lt;/span&gt; 
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_pixelsPerMercatorPixel&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This calculates how far the camera should be from the map center based on the FOV, ensuring the perspective projection renders correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Interactive Field of View Controller
&lt;/h2&gt;

&lt;p&gt;Want to let users adjust FOV dynamically? Here's a complete example with a slider you can check on on Codepen.&lt;/p&gt;

&lt;p&gt;

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


&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Isn't This a Public API?
&lt;/h2&gt;

&lt;p&gt;You might wonder why Mapbox doesn't officially expose FOV control. A few reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Complexity&lt;/strong&gt;: Changing FOV affects tile loading, symbol placement, and many other rendering systems&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance&lt;/strong&gt;: Non-standard FOV values can impact rendering performance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistency&lt;/strong&gt;: A fixed FOV ensures consistent user experience across applications&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintenance&lt;/strong&gt;: Supporting arbitrary FOV would increase API surface area and testing burden&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Risks
&lt;/h2&gt;

&lt;p&gt;Before you rush to implement this, understand the risks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⚠️ &lt;strong&gt;Breaking changes&lt;/strong&gt;: Internal APIs can change without warning in minor/patch releases&lt;/li&gt;
&lt;li&gt;⚠️ &lt;strong&gt;Unsupported behavior&lt;/strong&gt;: You may encounter rendering artifacts or unexpected behavior&lt;/li&gt;
&lt;li&gt;⚠️ &lt;strong&gt;No guarantees&lt;/strong&gt;: Mapbox won't provide support for issues arising from this technique&lt;/li&gt;
&lt;li&gt;⚠️ &lt;strong&gt;Version locked&lt;/strong&gt;: You'll need to test across Mapbox versions if you update&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Alternative: The FreeCamera API
&lt;/h2&gt;

&lt;p&gt;For production use cases involving advanced camera control, check out Mapbox's official &lt;a href="https://docs.mapbox.com/mapbox-gl-js/api/properties/#freecameraoptions" rel="noopener noreferrer"&gt;FreeCamera API&lt;/a&gt;. While it doesn't expose FOV directly, it provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Full camera position control&lt;/li&gt;
&lt;li&gt;Orientation adjustments&lt;/li&gt;
&lt;li&gt;Supported and stable API&lt;/li&gt;
&lt;li&gt;Works with all Mapbox features&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Exploring internal APIs like &lt;code&gt;Transform&lt;/code&gt; is a fantastic way to understand how sophisticated mapping libraries work under the hood. The FOV control technique shown here opens creative possibilities for enthusiasts and experimental projects.&lt;/p&gt;

&lt;p&gt;Just remember: &lt;strong&gt;with great power comes great responsibility&lt;/strong&gt;. Use internal APIs wisely, always have a fallback, and consider this a learning exercise rather than a production strategy.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Tested with Mapbox GL JS v3.18.1. Your mileage may vary with other versions.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/mapbox/mapbox-gl-js" rel="noopener noreferrer"&gt;Mapbox GL JS GitHub Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.mapbox.com/mapbox-gl-js" rel="noopener noreferrer"&gt;Mapbox GL JS Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>frontend</category>
      <category>javascript</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Build quickly with npm create @mapbox/web-app</title>
      <dc:creator>Andrew Sepic</dc:creator>
      <pubDate>Fri, 12 Dec 2025 15:45:44 +0000</pubDate>
      <link>https://dev.to/mapbox/build-quickly-with-npm-create-mapboxweb-app-d98</link>
      <guid>https://dev.to/mapbox/build-quickly-with-npm-create-mapboxweb-app-d98</guid>
      <description>&lt;p&gt;The Mapbox Developer Relations team recently released a new developer tool to help web developers quickly scaffold &lt;a href="https://docs.mapbox.com/mapbox-gl-js/" rel="noopener noreferrer"&gt;Mapbox GL JS&lt;/a&gt; into their favorite frontend frameworks.  You can now go from zero to hero with a map in your app in about 10 seconds. 🚀&lt;/p&gt;

&lt;p&gt;To get started, ensure you have &lt;a href="https://npmjs.org" rel="noopener noreferrer"&gt;&lt;code&gt;npm&lt;/code&gt;&lt;/a&gt; installed and run the command in your terminal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm create @mapbox/web-app 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Requires &lt;a href="https://nodejs.org" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; &lt;code&gt;v20+&lt;/code&gt;.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this is helpful
&lt;/h2&gt;

&lt;p&gt;If you work with &lt;a href="https://docs.mapbox.com/mapbox-gl-js/" rel="noopener noreferrer"&gt;Mapbox GL JS&lt;/a&gt; in modern JavaScript frameworks, you'll know that spinning up a map in your app can take more time and effort than you'd like.  This tool abstracts away project configuration, dependency management and lets developers get a simple map running very quickly.  This allows you to test ideas &amp;amp; features fast, without having to clone a local repo or project as a starting point.  &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%2Fff5vto5jehy9xhb9y1o0.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%2Fff5vto5jehy9xhb9y1o0.png" alt=" " width="800" height="443"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;npm create @mapbox/web-app&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;npm create @mapbox/web-app&lt;/code&gt; package is a CLI tool that uses &lt;a href="https://vite.dev" rel="noopener noreferrer"&gt;Vite&lt;/a&gt; templates to quickly build a project into the following frameworks: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Vanilla JS (with &amp;amp; without a bundler)&lt;/li&gt;
&lt;li&gt;React&lt;/li&gt;
&lt;li&gt;Vue&lt;/li&gt;
&lt;li&gt;Svelte&lt;/li&gt;
&lt;li&gt;Angular&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can also also add interactive search to your map by adding &lt;a href="https://docs.mapbox.com/mapbox-search-js" rel="noopener noreferrer"&gt;Mapbox Search JS&lt;/a&gt; via the install prompts.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The CLI tooling asks 4 questions:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Which framework do you want to use? &lt;/li&gt;
&lt;li&gt;Choose a project name..&lt;/li&gt;
&lt;li&gt;Access Token - Insert your public Mapbox access token and the tooling will pass this into the appropriate local &lt;code&gt;.env&lt;/code&gt; file. Get yours from your &lt;a href="https://console.mapbox.com/" rel="noopener noreferrer"&gt;developer console&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;Add Search JS (or not)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After answering those questions you'll have a full-screen Mapbox GL JS map loading in the browser within seconds, ready for you to customize. 🔥&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%2Ffthgzgg4xpuwfofyato6.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%2Ffthgzgg4xpuwfofyato6.png" alt=" " width="800" height="525"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;Now that you have a full screen map loaded, where do you want to go next?  If you are a new developer using Mapbox, you can expand this simple application by following one of these tutorials: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.mapbox.com/help/tutorials/use-mapbox-gl-js-with-react/" rel="noopener noreferrer"&gt;Use Mapbox GL JS in a React app&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.mapbox.com/help/tutorials/use-mapbox-gl-js-with-svelte/" rel="noopener noreferrer"&gt;Use Mapbox GL JS in a Svelte app&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.mapbox.com/help/tutorials/use-mapbox-gl-js-with-vue/" rel="noopener noreferrer"&gt;Use Mapbox GL JS in a Vue app&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.mapbox.com/help/tutorials/use-mapbox-gl-js-with-angular/" rel="noopener noreferrer"&gt;Use Mapbox GL JS in an Angular app&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The tutorials above demonstrate state management best practices for these frameworks, how to track &amp;amp; display map state into the application UI and how to trigger map events from the UI. &lt;/p&gt;

&lt;p&gt;If you are an experienced developer and are familiar with Mapbox products, you can browse our &lt;a href="https://docs.mapbox.com/help/tutorials/" rel="noopener noreferrer"&gt;tutorials&lt;/a&gt; for inspiration or search them for your use case.  &lt;/p&gt;

&lt;h2&gt;
  
  
  More Information on Github
&lt;/h2&gt;

&lt;p&gt;You can find more information about this CLI tool and report issues on &lt;a href="https://github.com/mapbox/create-web-app" rel="noopener noreferrer"&gt;Github&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>tooling</category>
      <category>npm</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
