StoryMap JS

Maps that tell stories.

Get started

Unofficial fork: this is an unofficial, unapproved fork of the original NUKnightLab/StoryMapJS repository and a proof of concept for an AI-based renovation using the model GLM 5.3 Flash. It is not endorsed by Northwestern University Knight Lab.

StoryMapJS tells stories on the web that highlight the locations of a series of events. This is a TypeScript rewrite of the original Knight Lab viewer: it renders storymap JSON (and IIIF manifests) on modern web maps — the authoring tool is not part of this repository.

There are a couple ways you can use a StoryMap.

  • Maps

    Add a slide for each place in your story. The viewer draws cartography from OpenStreetMap or custom tile servers, and renders the route between your places.

  • Really big images

    You can tell stories with large photographs, works of art, historic maps and other image files. Because it works best with pixel-dense files served over the IIIF Image API.

    Learn more about IIIF

Tips & tricks

  1. Keep it short. We recommend not having more than 20 slides for a reader to click through.
  2. Pick stories that have a strong location narrative. It does not work well for stories that need to jump around in the map.
  3. Write each event as a part of a larger narrative.
  4. Include events that build up to major occurrences — not just the major events.

Media sources

The viewer can pull in media from a variety of sources: Twitter/X, Flickr, YouTube, Vimeo, Dailymotion, Wikipedia, SoundCloud, Facebook, DocumentCloud, Juxtapose, Google Docs, images, audio, video, quotes and more.

Examples

Help

Need help? Start with the frequently asked questions below, then check the README and the migration guide.

If you don't find an answer there, open a GitHub issue. Please be clear with your question, include the storymap JSON you are loading and, if appropriate, a link to a page which shows the issue.

Find a bug? If you are confident you have found a bug, please report it as a GitHub issue. Be sure to include detailed instructions on how to reproduce it. Upstream bugs go to NUKnightLab/StoryMapJS.

Frequently asked questions

  • How do I install the viewer?

    npm install @projektemacher/storymapjs, then import the module and the stylesheet:

    import { StoryMap } from "@projektemacher/storymapjs";
    import "@projektemacher/storymapjs/css/storymap.css";
    
    new StoryMap("mapdiv", "my-storymap.json");
  • How do I embed a published storymap?

    Use the embed page in an iframe; the url parameter is resolved against the site root:

    <iframe src="/embed/index.html?url=my-storymap.json"
            width="100%" height="800"></iframe>
  • How can I link to a specific slide?

    The viewer keeps the current slide in the URL as a #slide-N hash. Open a storymap at …#slide-5 to start there, or use the start_at_slide option (also available as an embed URL parameter). Copy the URL at any time — it always points at the slide you are viewing.

  • Can the storymap play automatically?

    Set the autoplay option to the number of milliseconds each slide stays on screen (also available as an embed URL parameter):

    /embed/index.html?url=my-storymap.json&autoplay=5000

    Playback stops at the last slide, and any click, tap or keypress hands control back to the viewer.

  • How can I have a map without lines between points?

    Set show_lines: false in the storymap options (or use map_as_image for IIIF image stories, where lines are off by default).
  • How can I use a custom map?

    Set map_type to a tile URL template (e.g. "https://tile.example.org/{z}/{x}/{y}.png") or pass raw OpenLayers options via map_options — view, controls and interactions are all configurable.

  • How can I adjust the zoom?

    The viewer fits the map to all markers and zooms between places as the reader navigates (calculate_zoom). With calculate_zoom: true (the default) the calculated zoom overrides per-slide location.zoom values; set calculate_zoom: false to use your own location.zoom per slide. map_options.view accepts raw OpenLayers view options.

  • Can I change the fonts?

    Yes — ship a font theme with your storymap (font_css in the data, relative to the script) or reference a stock theme with font_css: "stock:knightlab". The compiled stock themes are resolved from the library's css/fonts/ directory (next to the bundled JS).

  • Can I use custom HTML in a slide?

    Yes — the slide text renders as HTML: links, images, lists and vco-note spans all work. Text without a <p> tag is wrapped in one automatically. The markup is sanitized first: formatting survives, scripts and event handlers are dropped, URLs are checked to be web URLs, and pasted <iframe> embeds are rebuilt sandboxed. Still, only load storymaps from sources you trust.

  • Can I limit the map to an area?

    Set map_bbox: [west, south, east, north] (lon/lat) in the options — the view center is constrained to the box, so the map cannot pan away from it. For IIIF image stories the coordinates are raw image pixels. To also limit the map to the left, visible area (opaque slide panel, no gradient over the map), set map_area: "left".
  • Does the viewer load external services by default?

    Yes, but set consent_required: true to ask for permission before anything is loaded from external services (media embeds, map tiles, external fonts). One dialog lists every service the story uses and offers allow all, decline all or a decision per service; services discovered later still ask on their slide. Denied services show a placeholder instead. Decisions are stored in your browser's local storage — clearing site data asks again.