Skip to content

ludos1978/miro-export-images

 
 

Repository files navigation

Miro board exporter

Exports Miro frames as full-detail SVGs or JSON using a headless Puppeteer browser.

Authentication

If accessing a private board, a personal token is required. To get a token, log in to Miro using a regular web browser, and then copy the value of the "token" cookie from developer tools. This is the token that should be used. If the board can be accessed without an account using a public link, the token is optional.

Note: For downloading images with --download-images, authentication is typically required even for boards accessible via public links, as image resources are protected by Miro's API authentication.

CLI

You can use this tool as a command-line tool.

Prerequisites

Installation

The CLI can be ran using npx with npx miro-export [options] (see options below). Alternatively, it's possible to install the package to the global scope with, for example, npm i -g miro-export.

Usage

Options:
  -t, --token <token>                Miro token
  -b, --board-id <boardId>           The board ID (required)
  -f, --frame-names <frameNames...>  The frame name(s), leave empty to export entire board
  -e, --export-format <format>       'svg' or 'json' (default: 'svg')
  --no-download-assets               Skip downloading all assets (images, fonts, embedded SVGs)
  -h, --help                         display help for command

The tool automatically:

  • Creates a timestamped folder: MiroBoard-{boardId}-{YYYYMMDD-HHmm}/
  • Saves the export as board.svg (or data.json for JSON format)
  • Downloads all assets by default (images, fonts, embedded SVGs)

Examples

# export entire board with all assets
miro-export -t XYZ -b uMoVLkx8gIc=
# Creates: ./MiroBoard-uMoVLkx8gIc-20241211-1430/board.svg

# using npx
npx miro-export -t XYZ -b uMoVLkx8gIc=

# export specific frame
miro-export -t XYZ -b uMoVLkx8gIc= -f "Frame 2"
# Creates: ./MiroBoard-uMoVLkx8gIc-20241211-1430/Frame_2/board.svg

# export multiple frames (each in separate folder)
miro-export -t XYZ -b uMoVLkx8gIc= -f "Design" "Wireframes"
# Creates: ./MiroBoard-uMoVLkx8gIc-20241211-1430/Design/board.svg
#          ./MiroBoard-uMoVLkx8gIc-20241211-1430/Wireframes/board.svg

# export as JSON format
miro-export -t XYZ -b uMoVLkx8gIc= -e json
# Creates: ./MiroBoard-uMoVLkx8gIc-20241211-1430/data.json

# export WITHOUT downloading assets (SVG only, no images/fonts)
miro-export -t XYZ -b uMoVLkx8gIc= --no-download-assets
# Creates: ./MiroBoard-uMoVLkx8gIc-20241211-1430/board.svg (without assets)

Automatic folder naming

The tool automatically generates timestamped folders for every export:

  • Format: MiroBoard-{boardId}-{YYYYMMDD-HHmm}
    • boardId: The cleaned board ID (alphanumeric characters only)
    • YYYYMMDD: Year, month, and day
    • HHmm: Hour and minute (24-hour format)

Example: ./MiroBoard-uMoVLkx8gIc-20241211-1430/

This ensures each export is uniquely identified and prevents accidental overwrites when exporting the same board multiple times. All exports are created in the current directory.

Capturing multiple frames

When specifying multiple frames with the -f switch (e.g., -f "Frame 2" "Frame 3"), each frame is automatically exported to its own subfolder within the timestamped export directory. This ensures each frame is captured separately without including content between frames.

Example structure:

MiroBoard-uMoVLkx8gIc-20241211-1430/
├── Frame_2/
│   └── board.svg
└── Frame_3/
    └── board.svg

Downloading images and assets

The tool automatically downloads all assets by default:

  1. Extract asset URLs - Scans the exported SVG for all embedded asset references:
    • Images: Background images, href attributes, embedded graphics
    • Fonts: WOFF2, WOFF, TTF, OTF files from @font-face declarations
    • Embedded SVGs: Nested SVG files and vector graphics
  2. Authenticate downloads - Uses the browser's authenticated session to fetch assets from Miro's API endpoints
  3. Auto-detect file types - Determines correct file extensions using URL analysis and magic byte detection:
    • Images: .png, .jpg, .gif, .webp, .svg
    • Fonts: .woff2, .woff, .ttf, .otf, .eot
  4. Generate proper filenames - Saves assets with resource ID-based names or URL-derived names
  5. Update SVG references - Replaces all remote URLs with local file paths in the exported SVG

Directory Structure:

MiroBoard-uMoVLkx8gIc-20241211-1430/
├── board.svg                    # Main SVG file with local asset references
├── 3458764514312409999.png      # Downloaded images
├── 3458764514312410000.jpg
├── 3458764516082584645.gif
├── 3458764515101541649.woff2    # Downloaded fonts
└── 3458764515101541650.svg      # Downloaded embedded SVGs

For multiple frames:

MiroBoard-uMoVLkx8gIc-20241211-1430/
├── Frame1/
│   ├── board.svg
│   ├── 123456789.png
│   ├── 987654321.woff2
│   └── 555666777.jpg
└── Frame2/
    ├── board.svg
    ├── 333444555.svg
    └── 888999000.png

Features:

  • Comprehensive asset detection - Finds images, fonts, and embedded SVGs using multiple regex patterns
  • Smart filename generation - Uses Miro resource IDs when available, falls back to URL-based naming
  • Robust file type detection - Combines URL extension analysis with magic byte detection
  • Retry logic - Automatically retries failed downloads up to 3 times
  • Rate limiting - Adds delays between downloads to avoid overwhelming the server
  • Progress tracking - Shows detailed progress for each asset download
  • Error handling - Reports successful and failed downloads with comprehensive summary

This is particularly useful for:

  • Creating completely offline-viewable exports with fonts and styling preserved
  • Archiving boards with all visual and typographic assets
  • Ensuring assets remain accessible even if original Miro URLs change
  • Converting Miro boards to self-contained documentation with proper font rendering
  • Preserving embedded SVG graphics and vector elements

Requirements:

  • Asset downloading is enabled by default for all exports
  • Use --no-download-assets to skip asset downloading
  • Asset downloading only works with SVG exports (not JSON)
  • Requires authentication token for private boards with protected assets

JSON export

The JSON export format is a Miro-internal representation of all the board objects. It is not a documented format, but it is quite easy to understand. The exported format is always an array of objects that have the field type as a discriminator. Depending on the type, fields change. Some of the types have been documented as TypeScript interfaces at miro-types.ts. For example, a sticky_note object could look like this:

{
  "type": "sticky_note",
  "shape": "square",
  "content": "<p>Test content</p>",
  "style": {
    "fillColor": "cyan",
    "textAlign": "center",
    "textAlignVertical": "middle"
  },
  "tagIds": [],
  "id": "3458764564249021457",
  "parentId": "3458764564247784511",
  "origin": "center",
  "relativeTo": "parent_top_left",
  "createdAt": "2023-09-11T12:45:00.041Z",
  "createdBy": "3458764537906310005",
  "modifiedAt": "2023-09-11T12:46:01.041Z",
  "modifiedBy": "3458764537906310005",
  "connectorIds": [],
  "x": 129.29101113436059,
  "y": 201.25587788616645,
  "width": 101.46000000000001,
  "height": 125.12
}

Programmatic usage

import { MiroBoard } from "miro-export";

await using miroBoard = new MiroBoard({
  boardId: "uMoVLkx8gIc=", // required
  token: "..." // optional
});

// get all board objects of type frame and with title "Frame 1"
const framesWithTitleFrame1 = await miroBoard.getBoardObjects(
  { type: "frame" }, // required (but empty object is OK too), limited field support
  { title: "Frame 1" } // optional additional filters
);

// get SVG of the first frame found above
const svgOfFrame1 = await miroBoard.getSvg([framesWithTitleFrame1[0].id]);

// if you can't use "await using" for disposal, you can also dispose manually:
// await miroBoard.dispose()
// this can also be used to close the browser at the middle of the current scope

Warning

Remember to dispose the instance to make sure the browser is closed and the process can exit. await using (as shown above) does this automatically, but is not supported in all environments and may not be the optimal choise in every case. Alternatively, miroBoard.dispose() may be called at any time to dispose of the instance manually.

Types for many of the common board object types has been provided in miro-types.ts.

About

Export Miro boards or frames as SVGs

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • TypeScript 97.6%
  • JavaScript 2.4%