Open-source project
mapbox/storytelling avatar
mapbox/storytelling

storytelling turns a Mapbox config.js into a scroll driven map story

Storytelling with maps template

679 stars301 forksHTMLBSD-3-Clause

At a glance

What is it?
The mapbox/storytelling template takes a story split into chapters, each pinned to a map position, and produces a static HTML and JavaScript page that scrolls through them. It is aimed at data journalists, and its honest limits are the Mapbox account it requires and the fact that the result cannot be embedded in an iFrame.
Who is it for?
Data journalists and digital storytellers who already have a Mapbox account and a story with sections that benefit from a map are the people this template serves, and the fact that the output is plain static HTML and JavaScript with no build step is the reason it still fits a small publication.
Can I use it commercially?
Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 116 days ago.
What is it written in?
Mainly HTML, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A story is a list of chapters, and each chapter owns a map view

The central abstraction in this template is small and worth understanding before anything else. A story is written as a series of sections called chapters, and each chapter is hooked to a particular view of a map. The documentation frames the whole project as a way to accelerate building out a map story, and the example given is the kind of graphic a data journalist makes when covering changing conditions in a population's demographics, the environment or an international conflict, and wants geographic context. A chapter is not just text. It carries a title, a description, an image path, an alignment, a hidden flag, and a location object with a center, a zoom, a pitch and a bearing. The location block is what makes the template distinctive: pitch and bearing mean a chapter can look along a mountainside or down a valley rather than straight down at a pin, and the camera animation is chosen per chapter between flyTo and other options. The map becomes the narrative spine, and the text scrolls beside it.

Per chapter hooks let a story react to its own transitions

The chapter object carries two arrays that most map libraries would not need: onChapterEnter and onChapterExit, plus an optional callback field. These are the extension points for a story that has to do something when the reader reaches or leaves a particular section, such as changing what a panel shows or triggering a side effect. Because they sit inside each chapter rather than in a global handler, the behaviour is attached to the narrative position where it makes sense. The same chapter object also carries a mapAnimation setting and a separate rotateAnimation boolean, so the transition into a view and the rotation of the map during it are controlled independently. That split matters in practice, since a chapter can want a smooth camera move without spinning the compass, and the template gives you both levers. The documentation does not document an error path for a malformed callback, so anyone relying on these hooks should test them against the live preview rather than assume a helpful failure.

Layers that show and hide as the story advances

The template can include and hide map data as the story sections transition, and the documentation suggests using this to highlight a neighborhood or to show satellite data from two different times. This is where a plain slide deck of map screenshots stops being enough, and where a custom Mapbox style starts to matter. You can optionally supply a custom Mapbox Style with layers styled in Studio, and then toggle each layer's opacity as the story moves. That means the data behind a story is authored once in Studio and revealed piece by piece, rather than reloaded from separate endpoints per chapter. The cost is a dependency on Mapbox Studio for anyone who wants custom layers: the documentation states that including custom map layers requires familiarity with Studio. The template itself does not rely on any particular CSS framework, fonts or images, and there are basic styles in the head of the HTML file that can be changed to match a site and story brand, so the visual layer of the page is yours to control even when the map layers are not.

The output is static files, which is why the page cannot be embedded

The output of this template is an HTML file and a JavaScript file, and they can be hosted on any web-accessible location with no extra code or infrastructure required. That constraint is the reason it has aged well for a template: no server, no build pipeline, no framework runtime to keep patched. The same simplicity produces the single most important limitation. The documentation states plainly that embedding the output as an iFrame in another page will not work as expected, and that the scroll driven interface requires the full page. Scroll position drives which chapter is active, and an iFrame is a viewport, not a page, so the scroll events the story depends on either do not arrive or arrive with the wrong geometry. If your publishing model requires the story to live inside a layout you do not control, this is a hard stop rather than an inconvenience, and the fix, publishing the story on its own URL, is a change to your site architecture rather than to the template.

Previewing locally needs the Live Server extension, not a file open

Running the story locally takes two steps in Visual Studio Code, and the reason the first step exists is technical rather than stylistic. The instructions are to install the Live Server extension and then open the index.html file with it, because serving the index.html from a local webserver prevents CORS errors and other issues that arise from opening an HTML file without a development server. The extension is installed from the Extensions tab in the editor, and once the HTML file is open, right-clicking in the editor window and choosing Open with Live Server starts a server and opens the default browser. The first thing you will see is a blank map, because config.js still contains the placeholder token rather than yours. There is no npm install, no bundler and no environment file, so the feedback loop is as short as it gets for a JavaScript project. The full clone-and-open sequence is the entire setup.

config.js carries the token, the theme and the map camera in one file

All configuration lives in config.js, and the documentation is direct about the syntax requirement: braces, brackets, commas and quotes are important, and config.js.template is the file to follow. The top level holds the style URL, the access token, whether markers are shown, the marker colour, the theme, a 3D terrain switch, and the story strings. The default style is Mapbox Standard, and the documentation points to the Styles API documentation page for more styles, or to Studio to create your own. Markers are optional per story and their colour is set with markerColor, defaulting to light blue. The theme is a simple choice between light and dark for the story text, and use3dTerrain turns terrain on, which pairs naturally with a chapter that has a pitch value. The block below is the shape of that configuration, and the chapters array is where the story goes.

js
{
    style: 'mapbox://styles/mapbox/streets-v11',
    accessToken: 'YOUR_MAPBOX_ACCESS_TOKEN',
    showMarkers: true,
    markerColor: '#3FB1CE',
    theme: 'light',
    use3dTerrain: false,
    title: 'The Title Text of this Story',
    subtitle: 'A descriptive and interesting subtitle to draw in the reader',
    byline: 'By a Digital Storyteller',
    footer: 'Source: source citations, etc.',

One chapter object holds the text, the camera and the transition

A chapter is a single object with a fixed set of keys, and the example given in the documentation shows every one of them: an id in slug style, an alignment of left, a hidden flag, a display title, an image path, a description, a location with center, zoom, pitch and bearing, the mapAnimation and rotateAnimation settings, an optional callback, and the two arrays for entering and leaving. Two details catch people out. The image path is relative, written as a path from the template, so the image has to travel with the files you publish. And the chapters array needs a comma between each section but no comma at the end, which is the classic JSON mistake when a story has five chapters and the fifth one refuses to parse. The example below is the shape of a chapter, with the description field shortened for readability.

js
{
    id: 'slug-style-id',
    alignment: 'left',
    hidden: false,
    title: 'Display Title',
    image: './path/to/image/source.png',
    location: {
        center: [-122.418398, 37.759483],
        zoom: 8.5,
        pitch: 60,
        bearing: 0
    },
    mapAnimation: 'flyTo',
    rotateAnimation: false,
    callback: '',
    onChapterEnter: [],
    onChapterExit: []
},

Mapbox Studio is the dependency, and the access token is the entry cost

Everything in this template points back to Mapbox as a service. To configure and publish a story you need a Mapbox access token, and the documentation notes the good practice of creating a separate token per map so traffic can be tracked per map. Beyond the token, the requirements are modest: a text editor, a place to publish static files, with GitHub Pages named as a free option, and a story, which the documentation calls the hardest part of the job and says is best when the sections benefit from a map. That last point is a design constraint on the writing, not just the software. A chapter whose text has no geographic dimension is padding, because the format spends the reader's scroll on camera movement. The template is BSD-3-Clause licensed, and it depends on no particular CSS framework, fonts or images, so the licensing question is the Mapbox terms of service and the access token rather than the template's own licence.

Editorial conclusion

Data journalists and digital storytellers who already have a Mapbox account and a story with sections that benefit from a map are the people this template serves, and the fact that the output is plain static HTML and JavaScript with no build step is the reason it still fits a small publication. It is the wrong choice for a team that needs the story embedded in an existing page, because the scroll driven interface requires the full page and embedding the output as an iFrame will not work as expected, and it is the wrong choice for a project that cannot take on a Mapbox access token and its terms. Before you build on it, check three things: that your chapters are ordered so the map movement carries the narrative, that the JSON punctuation in config.js matches the config.js.template exactly, and that the layer visibility transitions you need exist in the style you selected, since custom layers require familiarity with Mapbox Studio. The last push was on 2026-06-29 and the repository has no GitHub releases, so treat the main branch as the only upgrade channel and keep your own copy of config.js.

Frequently asked questions

How do I run the mapbox storytelling template locally?

Clone the repository, open the directory in Visual Studio Code, install the Live Server extension, then right-click the index.html file and choose Open with Live Server. Serving the file from a local webserver prevents CORS errors that come from opening an HTML file directly.

Why is my map blank when I open the template?

config.js still holds the placeholder YOUR_MAPBOX_ACCESS_TOKEN. Replace it with a token from your Mapbox account, and select the map style you want to use, the default being Mapbox Standard.

Can I embed a mapbox storytelling output in an iFrame?

No. The documentation states that embedding the output as an iFrame in another page will not work as expected, because the scroll driven interface requires the full page. The output has to be published on its own URL.

What is a chapter in the mapbox storytelling config?

A chapter is one section of the story with its own map view. It carries an id, alignment, hidden flag, title, image, description, a location with center, zoom, pitch and bearing, a mapAnimation setting, a rotateAnimation flag, an optional callback, and the onChapterEnter and onChapterExit arrays.

Does the mapbox storytelling template need a build step?

No. The output is an HTML file and a JavaScript file that can be hosted anywhere static files are served, with no extra code or infrastructure required. All configuration lives in config.js, and the repository is BSD-3-Clause licensed.

Official sources

  1. Issues
  2. License: BSD-3-Clause
  3. mapbox/storytelling on GitHub
  4. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/mapbox-storytelling.svg)](https://hysenlabs.com/projects/mapbox-storytelling)

Was this useful?