Enhance Miro to fit the way you work

Automate, extend, and customize Miro to fit your teamwork.
Create Apps that combine Rest API, Webhooks and Web-Plugins.
Bring your collaboration to the next level.

Get Started

Web-plugins features

Add buttons

You can add buttons on different elements of the Miro interface. You can add buttons asynchronously after performing relevant checks. For example, you might choose to add an Edit button only for users who have the right to edit the board. This example illustrates how to add buttons asynchronously.

Documentation on adding buttons is included in our extension points guidelines.

To apply a blue hover effect, use the SVG attribute fill="currentColor".

miro.onReady(() => {
  const icon24 = 'some svg here'

    extensionPoints: {
      bottomBar: {
        title: 'Demo app button',
        svgIcon: icon24,
        onClick: () => {
          alert('Bottom bar item has been clicked')

Render custom views

You can open a modal window, sidebar, bottom panel, or library where you can render a custom view.

For more information, see the extension point guidelines.

miro.board.ui.openLeftSidebar(iframeURL: string, options?: {width?: number}): Promise<any>
miro.board.ui.openLibrary(iframeURL: string, options: {title: string}): Promise<any>
miro.board.ui.openModal(iframeURL: string, options?: {maxWidth?: number; maxHeight?: number}): Promise<any>
miro.board.ui.openBottomPanel(iframeURL: string, options?: {width?: number; height?: number}): Promise<any>

Modify and read board content

A web-plugin can read, create, and modify content on the board. An item of board content is contained in a "widget". The Miro SDK currently supports operations on the following widget types:

  • text
  • sticker
  • shape
  • card
  • image
  • line

The web-plugin requires the boards:read scope to utilize methods that read board content, and the boards:write scope to modify board content.

// Create new sticker
await miro.board.widgets.create({type: 'sticker', text: 'Hello'})

// Get all stickers on the board
let allStickers = await miro.board.widgets.get({type: 'sticker'})
miro.board.widgets.create(widgets: {type: string; [index: string]: any}[]): Promise<IBaseWidget[]>
miro.board.widgets.get(filterBy?: Object): Promise<IBaseWidget[]>
miro.board.widgets.update(widgets: {id: string; [index: string]: any}[]): Promise<IBaseWidget[]>
miro.board.widgets.deleteById(widgetIds: InputWidgets): Promise<void>
miro.board.widgets.transformDelta(widgetIds: InputWidgets, deltaX: number | undefined, deltaY: number | undefined, deltaRotation: number | undefined): Promise<IBaseWidget[]>
miro.board.widgets.bringForward(widgetId: InputWidgets): Promise<void>
miro.board.widgets.sendBackward(widgetId: InputWidgets): Promise<void>

Rate limits

The Miro SDK enforces rate limits on widget operations. Each operation (create, update, or delete) on one widget costs one point. The current rate limit is 10,000 points per minute, summed over all widgets, and overall web-plugins, executing in a single client.

For example this operation costs 2 points:
miro.board.widgets.create([{type:'sticker', type:'sticker'}])

Modify and read tags on widgets

Stickers and card widgets can be tagged. These tags can be read and modified using the Miro SDK.

The API for tags is currently experimental

Experimental APIs might change at any time.

interface ITag {
    id: string
    title: string
    color: string | number
    widgetIds: string[]

// Supported operations
// Create sticker and card with tag 'Red tag' 
let widgets = await miro.board.widgets.create([
	{type: 'sticker', text: 'I am sticker'},
	{type: 'card', title: 'I am card'},
miro.board.tags.create({title: 'Red tag', color: '#F24726', widgetIds: widgets})

// Find all widgets with tag 'Red tag'
let tags = await miro.board.tags.get({title: 'Red tag'})

Listen to events on the board

To be notified of user interaction with the board, a web-plugin can create event listeners.

The Miro SDK supports the following events:

miro.addListener('WIDGETS_CREATED', widget => {

// For ALL_WIDGETS_LOADED event, we need to check if widgets
// are already loaded before subscription
async function onAllWidgetsLoaded(callback) {
  const areAllWidgetsLoaded = await miro.board.widgets.areAllWidgetsLoaded()
  if (areAllWidgetsLoaded) {
  } else {
    miro.addListener('ALL_WIDGETS_LOADED', callback)
onAllWidgetsLoaded(() => {
  console.log('all widgets are loaded')

Control the visibility of widgets for the current user

You can hide and show a widget for the current user by changing the clientVisible property of the widget. Widget visibility is applicable only in the user's web browser, and it does not affect other users of the board.

// Locally hide all stickers for the current user  
const allStickers = await miro.board.widgets.get({type: 'sticker'})
allStickers.forEach(s => {
	s.clientVisible = false

Store metadata in a widget

You can store custom data in a widget. This data is public. It can be read by any other web-plugin, and also retrieved using the REST API.

  "type": "sticker",
  "text": "some text",
  "metadata": {
    "{your_app_id}": {
      "hello": "world"

Disable editing on a widget

You can restrict all users from editing a widget by setting widget.capabilities.editable=false.

// Create a sticker that is not editable
  "type": "sticker",
  "text": "some text",
  "capabilities": {
    "editable": false

Enable drag and drop of items from a custom view

Custom views are often used to offer users a library of content they can add to a board, such as icons and images. You can enable drag and drop for such items using the initDraggableItemsContainer() method.

    <div id="box" style="background: red; width: 50px; height: 50px;"></div>
    async function createWidget(canvasX, canvasY) {
        const widget = (await miro.board.widgets.create({type: 'shape', x:canvasX || 0, y:canvasY || 0}))[0]
    const options = {
      draggableItemSelector: '#box',
      onClick: async (targetElement) => {
      getDraggableItemPreview: (targetElement) => { //drag-started
        return {url: HOTSPOT_PREVIEW}
      onDrop: (canvasX, canvasY) => {
        createWidget(canvasX, canvasY)
    miro.onReady(() => {
      miro.board.ui.initDraggableItemsContainer(document.body, options)  

Control the visible area of the board

A web-plugin can control the area of the board visible in the user's client. For example, zooming to a specific widget on the board.

// Zoom to selected widget
let selectedWidgets = await miro.board.selection.get()
let sticker = selectedWidgets[0]
miro.board.viewport.getViewport(): Promise<IRect>
miro.board.viewport.setViewport(viewport: IRect): Promise<IRect>
miro.board.viewport.setViewportWithAnimation(viewport: IRect): Promise<IRect>

miro.board.viewport.zoomToObject(objectId: InputWidget, selectObject?: boolean)

miro.board.viewport.setZoom(value: number): Promise<number>
miro.board.viewport.getZoom(): Promise<number>

Manage widget selection

A web-plugin can create a listener on the SELECTION_UPDATED event to be notified of changes in the set of widgets that the current user has selected. The web-plugin can then query for the list of selected widgets using miro.board.selection.get(), and modify the selection using miro.board.selection.selectWidgets().

// Select all stickers on the board
let allStickers = await miro.board.widgets.get({type: 'sticker'})

Display notifications

Web-plugins can display notifications to the user at the top of the board with these two methods:

  • showNotification(text: string)
  • showErrorNotification(text: string)

Add options to Miro settings

It is not currently possible for a web-plugin to add additional options to Miro settings.

Updated 7 days ago

Web-plugins features

Suggested Edits are limited on API Reference Pages

You can only suggest edits to Markdown body content, but not to the API spec.