Board UI
Methods
canOpenPanel(...)
() => Promise<boolean>
Checks if there are blocking elements before attempting to open a panel in the UI.
Panels that are already open from other sources can block these requests. It's recommended to check using miro.board.ui.canOpenPanel
before using miro.board.ui.openPanel
.
Example:
if (await miro.board.ui.canOpenPanel()) {
await miro.board.ui.openPanel({
url: 'show-this-page-in-the-panel.html',
height: 400,
});
}
openPanel(...)
( options: Identity<{ height?: number; title="url">url: string } & conditional> ) => Promise<{ waitForClose: () => Promise<Result | undefined> }>
Opens a panel on the current board.
The content displayed in the panel is fetched from the specified URL.
- The panel height corresponds to the current viewport height.
- Panel width: currently it's set to
368
dp, fixed, padding included.
You should implement your app to adapt to content area width changes between292
dp and320
dp, and to accommodate a fixed panel width between340
dp and368
dp, padding included. - Left padding:
24
dp - Right padding:
24
dp
When opening a panel, you can pass in data that can be accessed within the panel itself. Use the data
property within the options object to pass the necessary information.
Note: To get the data from inside the panel, use
getPanelData()
method.
The method for opening the panel returns a Promise
. This Promise
resolves to an object that includes a waitForClose()
method:
Promise<{ waitForClose: () => Promise<Result | undefined> }>;
The waitForClose()
method returns a Promise
that resolves to the result of the panel when it's closed, or undefined
if there is no result.
The result is a value that is passed as a parameter into closePanel()
method.
Note: When a user manually closes a panel, the promise returned by the
waitForClose()
method always resolves with anundefined
value.
Example:
// Example 1: Basic usage of openPanel
await miro.board.ui.openPanel({
url: 'show-this-page-in-the-panel.html',
});
// The panel is displayed on the board
// Example 2: Passing data into the panel
const dataForModal = "hello world";
await miro.board.ui.openPanel<string>({
data: dataForModal,
url: 'show-this-page-in-the-panel.html',
});
// Example 3: Waiting for the panel to close
const someData = { message: "my message" };
const { waitForClose } = await miro.board.ui.openPanel<{message: string}, string>({
data: someData,
url: 'show-this-page-in-the-panel.html',
});
// Inside the panel, you can call the following to close the panel and resolve the Promise:
// await miro.board.ui.closePanel('result');
console.log(await waitForClose()); // This will log: "result"
The content that apps display on modals and panels opens inside iframes.
iframes can request access to the following permissions:
microphone:listen
: access a user's microphone to record audio in an iframe.screen:record
: access a user's screen to record it in an iframe.webcam:record
: access a user's camera to record video.
See also:
Name | Type | Description |
---|---|---|
options | object | - |
options.url | string | Absolute or relative URL pointing to the content that you want to display in the panel. If you specify a relative URL, the URL path resolves relative to the app URL. The transport protocol must be HTTPS. |
options.data? | any | Any data that you want to pass directly to the panel and use it inside the panel itself. Check the getPanelData() method. |
getPanelData(...)
() => Promise<undefined | Data>
Retrieves the data associated with the currently open panel in the Miro board. This data is initially passed to the panel when it is opened using the miro.board.ui.openPanel({ data, ... })
method.
This method is typically called from within the panel itself to access any data that it needs to function correctly. If no data was passed when the panel was opened, calling getPanelData
will return a promise that resolves to undefined
.
Note:
getPanelData
will always return promise that resolves or toundefined
or to the actual passed data.
Example:
Consider the following scenario where a panel is opened with some data:
// Define some data to pass to the panel
const someData = { message: "my message" };
// Open a panel and pass the data to it
const { waitForClose } = await miro.board.ui.openPanel<{message: string}, string>({
data: someData,
url: 'show-this-page-in-the-panel.html',
height: 400,
});
Now, from within the panel, you can retrieve this data using getPanelData()
:
// Retrieve the data from within the panel
const data = await miro.board.ui.getPanelData<{message: string}>()
console.log(data) // Outputs: "{ message: "my message" }"
closePanel(...)
(result?: Result) => Promise<void>
Closes an open panel on the current board.
This method accepts an optional result
parameter. Providing this parameter results in the panel being resolved with the provided value. If you use the waitForClose()
method—returned by the miro.board.ui.openPanel(...)
—the promise returned by waitForClose()
will resolve, returning the value of result
.
TLDR; If you've passed a result value and close the panel, this value will be returned by the waitForClose()
promise. This is useful if you want to perform some action based on the result after the panel is closed.
ℹ️ Note:
- To close an open panel,
closePanel
must be executed afteropenPanel
. - The app that closes the panel with
closePanel
must be the same that opened it withopenPanel
.
Example:
// Open a panel
await miro.board.ui.openPanel({
url: 'show-this-page-in-the-panel.html',
height: 400,
});
// Close the open panel
await miro.board.ui.closePanel();
// Open a panel and wait for the result
const {waitForClose} = await miro.board.ui.openPanel({
url: 'show-this-page-in-the-panel.html',
height: 400,
});
async function logResult() {
const result = await waitForClose();
console.log(result) // This will log: "Hello world!"
}
logResult()
// Close the open panel with some result
await miro.board.ui.closePanel("Hello world!");
The content that apps display on modals and panels opens inside iframes.
iframes can request access to the following permissions:
microphone:listen
: access a user's microphone to record audio in an iframe.screen:record
: access a user's screen to record it in an iframe.webcam:record
: access a user's camera to record video.
See also:
canOpenModal(...)
() => Promise<boolean>
Checks if there are blocking elements before attempting to open a model in the UI.
Modals are blocking elements, meaning there can be only one modal displayed at a time for a user. If your operation happens at the same time as another Miro modal is being shown, your modal won't be displayed to the user. It's recommended to check using miro.board.ui.canOpenModal
before using miro.board.ui.openModal
.
Example:
if (await miro.board.ui.canOpenModal()) {
await miro.board.ui.openModal({
url: 'show-this-page-in-the-modal.html',
width: 600,
height: 400,
fullscreen: false,
});
}
openModal(...)
( options: Identity<{ fullscreen?: boolean; height?: number; title="url">url: string; width?: number } & conditional> ) => Promise<{ waitForClose: () => Promise<Result | undefined> }>
Opens a modal on the current board.
The content displayed in the modal is fetched from the specified URL.
The max. width and height of the modal correspond to the width and height of the current viewport.
The load timeout for the modal is 10 seconds.
You have the ability to pass data into a modal that you open. This data can then be accessed from within the modal itself. To pass data, use the data
property in the options object when opening it.
Note: to get the data from inside the modal you have to use
getModalData()
method.
The method for opening the modal returns a Promise
. This Promise
resolves to an object that includes a waitForClose()
method:
Promise<{ waitForClose: () => Promise<Result | undefined> }>;
The waitForClose()
method returns a Promise
that resolves to the result of the modal when it's closed, or undefined
if there is no result.
The result is a value that is passed as a parameter into closeModal()
method.
Note: promise returned by
waitForClose()
method will always be resolved withundefined
value in case of manual closing of modal by user.
Example:
// Example 1: Basic usage of openModal
await miro.board.ui.openModal({
url: "show-this-page-in-the-modal.html",
width: 600,
height: 400,
fullscreen: false,
});
// This will open a modal with the specified URL, width, and height. The modal will not be displayed in fullscreen mode.
// Example 2: Passing data into the modal
const dataForModal = "hello world";
await miro.board.ui.openModal<string>({
data: dataForModal,
url: "show-this-page-in-the-modal.html",
width: 600,
height: 400,
fullscreen: false,
});
// Example 3: Waiting for the modal to close
const someData = { message: "my message" };
const { waitForClose } = await miro.board.ui.openModal<{message: string}, string>({
data: someData,
url: "show-this-page-in-the-modal.html",
width: 600,
height: 400,
fullscreen: false,
});
// Inside the modal, you can call the following to close the modal and resolve the Promise:
// await miro.board.ui.closeModal('result');
console.log(await waitForClose()); // This will log: "result"
Name | Type | Description |
---|---|---|
options | object | - |
options.url | string | Absolute or relative URL pointing to the content that you want to display in the panel. If you specify a relative URL, the URL path resolves relative to the app URL. The transport protocol must be HTTPS. |
options.height? | number | Sets the height of the modal, in dp. The max. height of the modal corresponds to the height of the current viewport. Default: 600 |
options.width? | number | Sets the width of the modal, in dp. The max. width of the modal corresponds to the width of the current viewport. Default: 800 |
options.fullScreen? | boolean | Set it to:
false |
options.data? | any | Any data that you want to pass directly to the modal and use it inside the modal itself. Check the getModalData() method. |
getModalData(...)
() => Promise<undefined | Data>
Retrieves the data associated with the currently open modal in the Miro board. This data is initially passed to the modal when it is opened using the miro.board.ui.openModal({ data, ... })
method.
This method is typically called from within the modal itself to access any data that it needs to function correctly. If no data was passed when the modal was opened, calling getModalData
will return a promise that resolves to undefined
.
Note:
getModalData
will always return promise that resolves or toundefined
or to the actual passed data.
Example:
Consider the following scenario where a modal is opened with some data:
// Define some data to pass to the modal
const someData = { message: "my message" };
// Open a modal and pass the data to it
const { waitForClose } = await miro.board.ui.openModal<{message: string}, string>({
data: someData,
url: "show-this-page-in-the-modal.html",
width: 600,
height: 400,
fullscreen: false,
});
Now, from within the modal, you can retrieve this data using getModalData()
:
// Retrieve the data from within the modal
const data = await miro.board.ui.getModalData<{message: string}>()
console.log(data) // Outputs: "{ message: "my message" }"
closeModal(...)
(result?: Result) => Promise<void>
Closes an open modal on the current board.
This method accepts an optional result
parameter. Providing this parameter results in the modal being resolved with the provided value. If you use the waitForClose()
method—returned by miro.board.ui.openModal(...)
—the promise returned by waitForClose()
will resolve, returning the value of result
.
TLDR; If you've passed a result value and close the modal, this value will be returned by the waitForClose()
promise. This is useful if you want to perform some action based on the result after the modal is closed.
ℹ️ Note:
- To close an open modal,
closeModal
must be executed afteropenModal
. - The app that closes the modal with
closeModal
must be the same app that opened it withopenModal
.
Example:
// Open a modal
await miro.board.ui.openModal({
url: 'show-this-page-in-the-modal.html',
height: 400,
width: 600,
fullscreen: false,
});
// Close the open modal
await miro.board.ui.closeModal();
// Open a modal and wait for the result
const {waitForClose} = await miro.board.ui.openModal({
url: 'show-this-page-in-the-modal.html',
height: 400,
width: 600,
fullscreen: false,
});
async function logResult() {
const result = await waitForClose();
console.log(result) // This will log: "Hello world!"
}
logResult()
// Close the open modal with some result
await miro.board.ui.closeModal("Hello world!");
on(...)
(event: 'drop', handler: (event: DropEvent) => void) => void
on
property
on
propertymiro.board.ui.on(event, handler);
If you want your app to react to an event by executing a function, you can use the on
property to subscribe to events.
The on
property subscribes the app to listen to an event. When the event fires, the event handler executes a function to perform an action.
To subscribe to an event and its handler, pass to the on
property:
- The event that your app should listen to.
- The event handler that the app needs to call when the event fires.
💡 To unsubscribe from an event and its handler, use the off
property.
Supported events
Event type | Event handler | Event fires when... | |
---|---|---|---|
drop | (event: DropEvent) | An HTML element is dropped from an open panel to the board. See also Add drag and drop to your app. | |
icon:click | () | An app icon is clicked. See also Add clicking on an icon to your app. | |
app_card:open | (event: AppCardOpenEvent) | The app card status icon is connected , the icon is clicked, and it enables opening a modal with the app card detail view. | |
app_card:connect | (event: AppCardConnectEvent) | The app card status icon is disconnected , the icon is clicked, and it enables connecting an app card with a corresponding data source. | |
selection:update | (event: SelectionUpdateEvent) | An area on the board is selected. | |
online_users:update | (event: OnlineUsersUpdateEvent) | The number of currently online board users changes. | |
items:create | (event: ItemsCreateEvent) | New items are created on the board. | |
experimental:items:update | (event: ItemsUpdateEvent) | Existing items are updated on the board. | |
items:delete | (event: ItemsDeleteEvent) | Existing items are deleted from the board. | |
custom:${string} | (event: CustomEvent) | A custom action on a board item is initiated. |
Dispatching events
In general, when an app subscribes to an event, the event is dispatched to all iframes:
This behavior makes it easy to subscribe to an event from any of these iframes, without worrying about which iframe the event is dispatched to.
ℹ️ Note:
icon:click
is dispatched only to the headless/main iframe.
Typically,icon:click
is used to open a panel or a modal when a user clicks the app icon on the app toolbar or the app toolbar panel.
drop
event
drop
eventon(event: 'drop', handler: (event: DropEvent) => void): void
When an app subscribes to this event, it's dispatched to all iframes.
drop
registers the event associated with dropping one or more selected items on the board.
The event handler has the following properties:
Property | Type | Description |
---|---|---|
x | Number | Coordinate that defines the horizontal position of the HTML element to drop on the board. |
y | Number | Coordinate that defines the vertical position of the HTML element to drop on the board. |
target | Element interface | The HTML element that is the recipient of the drop event. |
Example:
/** When the selected HTML element is dropped on the board:
* 1. A sticky note is created.
* 2. The text of the HTML element is assigned to the
* 'content' property of sticky note.
*/
miro.board.ui.on('drop', async ({x, y, target}) => {
await miro.board.createStickyNote({
x,
y,
content: target.innerText,
});
});
You can provide a custom preview image for drag & drop using data attributes on elements with class="miro-draggable"
:
data-drag-preview
- Specify the URL of the preview image to use during dragging.data-drag-preview-width
- (Optional) Set thewidth
of the preview image in pixels.data-drag-preview-height
- (Optional) Set theheight
of the preview image in pixels.
Preview image size behavior:
- If no
width
orheight
is set, the original image size will be used. - If only
width
orheight
is set, that dimension will be used and the other dimension will be calculated to maintain aspect ratio. - If both
width
andheight
are set, those dimensions will be applied and aspect ratio will be ignored.
A valid URL pointing to or representing the image. Possible formats:
- A publicly available, absolute URL to an image.
- A base64-encoded data URL:
data:image/<image-file-format>;base64,<base64-encoded-image>
.
Example:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>My panel with draggable items</title>
<!-- Loads the Miro Web SDK -->
<script src="https://miro.com/app/static/sdk/v2/miro.js"></script>
<!-- Loads the companion JS file with the logic -->
<script src="app.js"></script>
</head>
<body>
<!-- The 'miro-draggable' class makes HTML items draggable -->
<!-- The `data-drag-preview` attribute changes preview image during dragging of element -->
<div class="miro-draggable" data-drag-preview="https://amazing-images.com/categories/cats/cute-fluffy-kitten.png">
Kitten
</div>
<!-- The `data-drag-preview-width` attribute changes preview image width. The `data-drag-preview-height` will work in the same way -->
<div
class="miro-draggable"
data-drag-preview-width="100"
data-drag-preview="https://amazing-images.com/categories/dogs/beautiful-dog.jpg"
>
Dog
</div>
<!-- The `data-drag-preview-width` + `data-drag-preview-height` force apply size and ignore aspect ratios of image -->
<div
class="miro-draggable"
data-drag-preview-width="100"
data-drag-preview-height="100"
data-drag-preview="https://amazing-images.com/categories/dragons/scary-dragon.svg"
>
Dragon
</div>
</body>
</html>
icon:click
event
icon:click
eventon(event: 'icon:click', handler: () => void) => void
When an app subscribes to this event, it's dispatched only to the headless/main iframe.
icon:click
registers click events on icons.
When a user clicks an icon, the registered event handler for icon:click
is called.
Typically, icon:click
is used to open a panel, or to display a modal.
Example:
/** When a user clicks the icon:
* 1. The openPanel method is called
* 2. The method opens the HTML page: `panel.html`
*/
miro.board.ui.on('icon:click', async () => {
await miro.board.ui.openPanel({
url: 'panel.html',
});
});
app_card:open
event
app_card:open
eventon(event: 'app_card:open', handler: (event: AppCardOpenEvent) => void): void;
When an app subscribes to this event, it's dispatched to all iframes.
app_card:open
registers click events to open an app card from compact to detail view.
When a user clicks the icon to expand an app card to view it in detail, the registered event handler for app_card:open
is called.
Typically, app_card:open
is used to open a modal displaying the custom fields of the app card and their content.
Example:
/** When a user clicks the icon that expands an app card to view it in detail:
* 1. The 'openModal' method is called
* 2. The method opens a modal to display the specific app card content fetched from the URL
*/
// Listen to the 'app_card:open' event
miro.board.ui.on('app_card:open', (event) => {
console.log('Subscribed to app card open event', event);
const {appCard} = event;
// Build a URL containing the app card ID.
// You pass this URL with the 'openModal()' method below.
// The code in the modal uses the 'appCardId' URL query parameter
// to identify which app card was opened.
const url = `https://my.app.example.com/modal.html?appCardId=${appCard.id}`;
// Open the modal to display the content of the fetched app card
miro.board.ui.openModal({
url,
});
});
app_card:connect
event
app_card:connect
eventon(event: 'app_card:connect', handler: (event: AppCardConnectEvent) => void): void;
When an app subscribes to this event, it's dispatched to all iframes.
By default, newly created app cards have a disconnected
status, unless the app card constructor sets a different value.
To connect an app card to a corresponding data source in an external application, an app must listen to the app_card:connect
event.
When an app listens to the app_card:connect
event:
- The
disconnected
icon is clickable. - On hovering over the icon, a tooltip is displayed to notify users that they can click the icon to connect the app card to a data source.
- When users click the icon, the
app_card:connect
event fires. - The event handler needs to include at least the logic to:
- Retrieve the data source that the app card maps to.
- Sync data to populate the app card fields with any updated information.
- Update the app card status from
disconnected
toconnected
. sync()
the app card to propagate the changes to the board.
If the app listens also to the app_card:open
event, it can react to it; typically, by opening a modal to display the app card detail view.
Example:
/** In a typical flow:
* 1. The app creates an app card.
* The 'disconnected' status icon is not clickable, yet.
* 2. The app listens to the 'app_card:connect' event.
* The 'disconnected' status icon is clickable.
* 3. The app listens to the 'app_card:open' event.
* 4. When users click the 'disconnected' status icon, a tooltip prompts them to connect the app card to its data source.
* 5. When the app card is connected, its status icon changes to 'connected'.
* 6. Now the app can open a modal to display the app card detail view.
*/
// Create an app card
const appCard = await miro.board.createAppCard({
title: 'This is the title of the app card',
// Default status of new app cards
status: 'disconnected',
});
// Listen to the 'app_card:connect' event
miro.board.ui.on('app_card:connect', (event) => {
console.log('Connect the app card to its data source');
const appcard = event.appCard;
// Update the app card status to 'connected'
appcard.status = 'connected';
// Propagate the app card updates to the board
appcard.sync();
});
// Listen to the 'app_card:open' event
miro.board.ui.on('app_card:open', (event) => {
console.log('Subscribed to app card open event', event);
// URL containing the app card ID.
// The content is displayed inside the modal
const url = `https://my.app.example.com/modal.html?appCardId=${appCard.id}`;
// Open the modal to display the content of the fetched app card
miro.board.ui.openModal({
url,
});
});
Figure 1. Newly created card, or duplicate app card through manual copying and pasting on the board UI. The app card status is disconnected. The tooltip notifies about the missing connection. The status icon isn't clickable.
Figure 2. The app card status is disconnected
. The tooltip prompts to connect it to its data source. The status icon is clickable.
Figure 3. The app card status is connected
. The app listens to the app_card:open
event. When the icon is clicked, the event fires. Typically, it opens a modal to display the app card detail view.
selection:update
event
selection:update
eventon(event: 'selection:update', handler: (event: SelectionUpdateEvent) => void): void;
When an app subscribes to this event, it's dispatched to all iframes.
selection:update
registers the event associated with updating the content of the current selection on the board.
When a user selects an area on the board, the registered event handler for selection:update
is called.
The event contains an array with the selected board items.
If the selected area doesn't include any board items, the array is empty.
If the grouped items are selected, the group entity for those items will also be returned.
When individual items within a group are selected with a double-click, the group entity will not be returned as part of this method.
You can add logic to perform actions on the selection, such as filtering specific item types, and then modifying them.
Example:
/** When a user clicks and selects multiple board items on a board:
* 1. The 'selection:update' method logs the selection to the developer console
* 2. A filter identifies sticky note items in the selection
* 3. The color of the sticky notes is changed to 'cyan'
*/
// Listen to the 'selection:update' event
miro.board.ui.on('selection:update', async (event) => {
console.log('Subscribed to selection update event', event);
console.log(event.items);
const selectedItems = event.items;
// Filter sticky notes from the selected items
const stickyNotes = selectedItems.filter((item) => item.type === 'sticky_note');
// Change the fill color of the sticky notes
for (const stickyNote of stickyNotes) {
stickyNote.style.fillColor = 'cyan';
await stickyNote.sync();
}
});
online_users:update
event
online_users:update
eventon(event: 'online_users:update', handler: (event: OnlineUsersUpdateEvent) => void): void
When an app subscribes to this event, it's dispatched to all iframes.
online_users:update
registers the event associated with a change in the number of users that are currently online on the board.
When a user joins or leaves the board, the registered event handler for online_users:update
is called.
The event contains an array with user IDs and names of the online users.
Your app can include logic to perform follow-up actions based on the change in online users.
For example, it can greet a new user that just joined the board.
Example:
/** When the number of online users changes, identify the
* new online users and greet them with a notification.
*/
let currentOnlineUsers = [];
// Listen to the 'online_users:update' event.
await miro.board.ui.on('online_users:update', async (event) => {
console.log('Subscribed to the update of online users');
console.log('Online users: ', event.users);
const onlineUsers = event.users;
// Identify the new online users.
const newUsers = onlineUsers.filter((user) => !currentOnlineUsers.find((u) => u.id === user.id));
console.log('New users:', newUsers);
// Greet the new online users.
for (const newUser of newUsers) {
await miro.board.notifications.showInfo(`Hello, ${newUser.name}!`);
}
currentOnlineUsers = onlineUsers;
});
items:create
event
items:create
eventon(event: 'items:create', handler: (event: ItemsCreateEvent) => void): void;
When an app subscribes to this event, it's dispatched to all iframes.
items:create
registers the event associated with the creation of a new item on the board.
When a user creates a new item on a board, the registered event handler for items:create
is called.
The event contains an array with the created board items.
Your app can include logic to perform follow-up actions on the created items. For example, it can filter specific item types, and then process them by fetching or setting their properties.
ℹ️ Note:
- When creating a new item by copy-pasting or by duplicating an existing one,
items:create
isn't triggered.
Example:
/** When a user creates a new item on a board:
* 1. 'items:create' logs the created items to the developer console.
* 2. In the group of created items, a filter identifies sticky notes.
* 3. The color of the sticky notes is set to 'cyan'.
*/
// Listen to the 'items:create' event.
miro.board.ui.on('items:create', async (event) => {
console.log('Subscribed to the creation of new board items', event);
console.log(event.items);
const createdItems = event.items;
// Filter sticky notes from the created items.
const stickyNotes = createdItems.filter((item) => item.type === 'sticky_note');
// Change the fill color of the sticky notes.
for (const stickyNote of stickyNotes) {
stickyNote.style.fillColor = 'cyan';
await stickyNote.sync();
}
});
Experimental
experimental:items:update
event
experimental:items:update
eventon(event: 'experimental:items:update', handler: (event: ItemsUpdateEvent) => void): void;
When an app subscribes to this event, it's dispatched to all iframes.
experimental:items:update
registers the event associated with updating an item on the board.
When a user updates one or more items on the board, the registered event handler for experimental:items:update
is called.
The event contains an array with the updated board items.
Your app can include logic to perform follow-up actions on the updated items. For example, it can log a list of the updated board items.
ℹ️ Note:
- Currently,
experimental:items:update
fires only when items are moved on the board, when an item dimensions are resized, when an item is rotated on the board, and when updating the scale of an item.
In the future, the event will also fire when updating other item data.
Example:
/**
* When a user updates one or more items on the board:
* 'experimental:items:update' logs all the updated board items to the developer console.
*/
// Listen to the 'experimental:items:update' event.
miro.board.ui.on('experimental:items:update', async (event) => {
console.log('Subscribed to updates of board items', event);
console.log('Updated items: ', event.items);
});
items:delete
event
items:delete
eventon(event: 'items:delete', handler: (event: ItemsDeleteEvent) => void): void;
When an app subscribes to this event, it's dispatched to all iframes.
items:delete
registers the event associated with deleting an item from the board.
When a user deletes one or more items from the board, the registered event handler for items:delete
is called.
The event contains an array with the deleted board items.
Your app can include logic to perform follow-up actions on the deleted items. For example, it can log a list of the deleted board items.
Example:
/**
* When a user deletes one or more items from the board:
* 'items:delete' logs all deleted board items to the developer console.
*/
// Listen to the 'items:delete' event.
miro.board.ui.on('items:delete', async (event) => {
console.log('Subscribed the deletion of board items', event);
console.log('Deleted items: ', event.items);
});
custom:${string}
event
custom:${string}
eventon(event: `custom:${string}`, handler: (event: CustomEvent) => void): void;
When an app subscribes to this event, it's dispatched to all iframes.
custom:${string}
events implement custom actions in an app.
Web SDK custom actions behave like standard Web SDK events:
- The app subscribes to a custom action event with the
on
property. - When the custom action event is no longer necessary, the app unsubscribes from it with the
off
property.
Custom event naming
Custom event names always start with the custom:
prefix: custom:${string}
${string}
must match the value assigned to the event
property when the app registers the custom action with the miro.board.experimental.action.register
method.
- The event name can contain only lowercase alphabetic characters and hyphens (
^[a-z]+(-[a-z]+)\*$
). - It cannot contain spaces.
- It cannot be longer than 30 characters.
off(...)
(event: 'drop', handler: (event: DropEvent) => void) => void
off
property
off
propertymiro.board.ui.off(event, handler);
When an app no longer needs to listen to an event to trigger an event handler, it can use the off
property to unsubscribe from it.
To unsubscribe from an event and its handler, pass to the off
property:
- The event whose handler you want your app to unsubscribe from.
- The event handler that you previously registered with the
on
property, and that your app no longer needs to listen to.
💡 To subscribe to an event and its handler, use the on
property.
Supported events
See the supported events for the on
property.
drop
unsubscribe
drop
unsubscribeoff(event: 'drop', handler: (event: DropEvent) => void): void
Enables unsubscribing from a drop
event handler.
To unsubscribe, pass to the off
property:
- The
drop
event. - The event handler that you previously registered with the
on
property, and that your app no longer needs to listen to.
Example:
// Add a 'drop' event handler to drag and drop images.
const drop = async (event: DropEvent) => {
const {x, y, target} = event;
if (target instanceof HTMLImageElement) {
const image = await miro.board.createImage({
x,
y,
url: target.src,
});
}
};
// Register the 'drop' event so that the app listens to it.
miro.board.ui.on('drop', drop);
// Unsubscribe from the 'drop' handler.
// The app no longer creates image items on drag and drop.
miro.board.ui.off('drop', drop);
icon:click
unsubscribe
icon:click
unsubscribeoff(event: 'icon:click', handler: () => void) => void
Enables unsubscribing from an icon:click
event handler.
To unsubscribe, pass to the off
property:
- The
icon:click
event. - The event handler that you previously registered with the
on
property, and that your app no longer needs to listen to.
Example:
// Add an 'iconClick' event handler to open a panel upon clicking an icon.
const iconClick = async () => {
await miro.board.ui.openPanel({
url: 'panel.html',
});
};
// Register the 'icon:click' event so that the app listens to it.
miro.board.ui.on('icon:click', iconClick);
// Unsubscribe from the 'icon:click' event handler.
// The app no longer enables opening a panel when clicking an icon.
miro.board.ui.off('icon:click', iconClick);
app_card:open
unsubscribe
app_card:open
unsubscribeoff(event: 'app_card:open', handler: (event: AppCardOpenEvent) => void): void;
Enables unsubscribing from an app_card:open
event handler.
To unsubscribe, pass to the off
property:
- The
app_card:open
event. - The event handler that you previously registered with the
on
property, and that your app no longer needs to listen to.
Example:
// Create an app card.
const appCard = await miro.board.createAppCard({
title: 'This is the title of the app card',
status: 'disconnected',
});
// Add an 'appCardOpen' event handler for the 'app_card:open' event.
const appCardOpen = async (event: AppCardOpenEvent) => {
const appcard = event.appCard;
const url = `https://my.app.example.com/modal.html?appCardId=${appCard.id}`;
miro.board.ui.openModal({
url,
});
};
// Register the 'app_card:open' event so that the app listens to it.
miro.board.ui.on('app_card:open', appCardOpen);
// Unsubscribe from the 'app_card:open' event handler.
// The app no longer enables opening a modal with the detail view of an app card.
miro.board.ui.off('app_card:open', appCardOpen);
app_card:connect
unsubscribe
app_card:connect
unsubscribeoff(event: 'app_card:connect', handler: (event: AppCardConnectEvent) => void): void;
Enables unsubscribing from the app_card:connect
event and its handler.
To unsubscribe, pass to the off
property:
- The
app_card:connect
event. - The event handler that you previously registered with the
on
property, and that your app no longer needs to listen to.
Example:
// Create an app card.
const appCard = await miro.board.createAppCard({
title: 'This is the title of the app card',
status: 'disconnected',
});
// Add an 'appCardConnect' event handler for the 'app_card:connect' event.
const appCardConnect = async (event: AppCardConnectEvent) => {
const appcard = event.appCard;
appcard.status = 'connected';
appcard.sync();
};
// Register the 'app_card:connect' event so that the app listens to it.
miro.board.ui.on('app_card:connect', appCardConnect);
// Unsubscribe from the 'app_card:connect' event handler.
// The app no longer enables connecting an app card to a data source.
miro.board.ui.off('app_card:connect', appCardConnect);
selection:update
unsubscribe
selection:update
unsubscribeoff(event: 'selection:update', handler: (event: SelectionUpdateEvent) => void): void;
Enables unsubscribing from a selection:update
event handler.
To unsubscribe, pass to the off
property:
- The
selection:update
event. - The event handler that you previously registered with the
on
property, and that your app no longer needs to listen to.
Example:
// Add a 'selectionUpdate' event handler to update the color of sticky notes included in a selection.
const selectionUpdate = async (event: SelectionUpdateEvent) => {
const selectedItems = event.items;
const stickyNotes = selectedItems.filter((item) => item.type === 'sticky_note');
for (const stickyNote of stickyNotes) {
stickyNote.style.fillColor = 'cyan';
await stickyNote.sync();
}
};
// Register the 'selection:update' event so that the app listens to it.
miro.board.ui.on('selection:update', selectionUpdate);
// Unsubscribe from the 'selection:update' event handler.
// The app no longer enables updating the color of the sticky notes included in a selection to cyan.
miro.board.ui.off('selection:update', selectionUpdate);
online_users:update
unsubscribe
online_users:update
unsubscribeoff(event: 'online_users:update', handler: (event: OnlineUsersUpdateEvent) => void): void
Enables unsubscribing from an online_users:update
event handler.
To unsubscribe, pass to the off
property:
- The
online_users:update
event. - The event handler that you previously registered with the
on
property, and that your app no longer needs to listen to.
Example:
// Add an 'online_users:update' event handler to greet new online users joining the board.
let currentOnlineUsers = [];
const onlineUsersUpdate = async (event) => {
const onlineUsers = event.users;
const newUsers = onlineUsers.filter((user) => !currentOnlineUsers.find((u) => u.id === user.id));
for (const newUser of newUsers) {
await miro.board.notifications.showInfo(`Hello, ${newUser.name}!`);
}
currentOnlineUsers = onlineUsers;
};
// Register the 'online_users:update' event so that the app listens to it.
await miro.board.ui.on('online_users:update', onlineUsersUpdate);
// Unsubscribe from the 'online_users:update' event handler.
// The app no longer greets new online users.
await miro.board.ui.off('online_users:update', onlineUsersUpdate);
items:create
unsubscribe
items:create
unsubscribeoff(event: 'items:create', handler: (event: ItemsCreateEvent) => void): void;
Enables unsubscribing from an items:create
event handler.
To unsubscribe, pass to the off
property:
- The
items:create
event. - The event handler that you previously registered with the
on
property, and that your app no longer needs to listen to.
Example:
// Add an 'itemsCreate' event handler to update the color of user-created sticky notes.
const itemsCreate = async (event: ItemsCreateEvent) => {
const createdItems = event.items;
const stickyNotes = createdItems.filter((item) => item.type === 'sticky_note');
for (const stickyNote of stickyNotes) {
stickyNote.style.fillColor = 'cyan';
await stickyNote.sync();
}
};
// Register the 'items:create' event so that the app listens to it.
miro.board.ui.on('items:create', itemsCreate);
// Unsubscribe from the 'items:create' event handler.
// The app no longer updates the color of user-created sticky notes.
miro.board.ui.off('items:create', itemsCreate);
Experimental
experimental:items:update
unsubscribe
experimental:items:update
unsubscribeoff(event: 'experimental:items:update', handler: (event: ItemsUpdateEvent) => void): void;
Enables unsubscribing from an experimental:items:update
event handler.
To unsubscribe, pass to the off
property:
- The
experimental:items:update
event. - The event handler that you previously registered with the
on
property, and that your app no longer needs to listen to.
Example:
// Add an 'itemsUpdate' event handler to log user-updated board items to the developer console.
const itemsUpdate = async (event: ItemsUpdateEvent) => {
console.log(event.items);
};
// Register the 'experimental:items:update' event so that the app listens to it.
miro.board.ui.on('experimental:items:update', itemsUpdate);
// Unsubscribe from the 'experimental:items:update' event handler.
// The app no longer enables logging user-updated board items.
miro.board.ui.off('experimental:items:update', itemsUpdate);
items:delete
unsubscribe
items:delete
unsubscribeoff(event: 'items:delete', handler: (event: ItemsDeleteEvent) => void): void;
Enables unsubscribing from an items:delete
event handler.
To unsubscribe, pass to the off
property:
- The
items:delete
event. - The event handler that you previously registered with the
on
property, and that your app no longer needs to listen to.
Example:
// Add an 'itemsDelete' event handler to log user-deleted board items to the developer console.
const itemsDelete = async (event: ItemsDeleteEvent) => {
console.log(event.items);
};
// Register the 'items:delete' event so that the app listens to it.
miro.board.ui.on('items:delete', itemsDelete);
// Unsubscribe from the 'items:delete' event handler.
// The app no longer enables logging user-deleted board items.
miro.board.ui.off('items:delete', itemsDelete);
custom:${string}
event
custom:${string}
eventoff(event: `custom:${string}`, handler: (event: CustomEvent) => void): void;
Enables unsubscribing from a custom event handler.
To unsubscribe, pass to the off
property:
- The
custom:${event}
event that you want your app to unsubscribe from. - The event handler that you previously registered with the
on
property, and that your app no longer needs to listen to.
All properties
Property | Type |
---|---|
canOpenModal(...) |
() => Promise<boolean> |
canOpenPanel(...) |
() => Promise<boolean> |
closeModal(...) |
(result?: Result) => Promise<void> |
closePanel(...) |
(result?: Result) => Promise<void> |
getModalData(...) |
() => Promise<undefined | Data> |
getPanelData(...) |
() => Promise<undefined | Data> |
off(...) |
(event: 'drop', handler: (event: DropEvent) => void) => void |
on(...) |
(event: 'drop', handler: (event: DropEvent) => void) => void |
openModal(...) |
( options: Identity<{ fullscreen?: boolean; height?: number; title="url">url: string; width?: number } & conditional> ) => Promise<{ waitForClose: () => Promise<Result | undefined> }> |
openPanel(...) |
( options: Identity<{ height?: number; title="url">url: string } & conditional> ) => Promise<{ waitForClose: () => Promise<Result | undefined> }> |
Updated 3 months ago