---
updatedAt: 2026-03-19T18:27:27.000Z
agentTools:
  projectIndex: https://developers.miro.com/llms.txt
---

# Connect the Miro Web SDK to a backend (Firebase)

Create a Node.js application that leverages the Miro Web SDK to send and retrieve data from a backend and populate a Miro board.

The app saves items on your board as templates in a [Firebase](https://console.firebase.google.com/) database, enabling users to reuse them by selecting them from the app panel. The complete project code is available on [GitHub](https://github.com/miroapp/app-examples/tree/main/examples/connect-firebase).

[block:image]
{
  "images": [
    {
      "image": [
        "https://files.readme.io/dc7e020-image.png",
        null,
        "Figure 1. Miro board showing shapes and a sticky note."
      ],
      "align": "center",
      "border": true,
      "caption": "Figure 1. Miro board showing shapes and a sticky note."
    }
  ]
}
[/block]

## Prerequisites

Before you start, you need the following:

* A Miro account to create a Miro app.
* A recent version of Node.js installed on your computer. The latest LTS version is recommended.
* A Firebase account to use a real-time database.

## Set up the Miro Node.js app

The easiest way to start using the Miro Web SDK is from the [Hello, World!](https://developers.miro.com/docs/build-your-first-hello-world-app) app. To begin creating this app:

1. Open a terminal window and go to the directory where you want to make your app.
2. Run `npx create-miro-app@latest`.
3. Select **Vanilla** as a framework.
4. Select **JavaScript** as a flavor.
5. Press **Enter** ↩.

These steps create a directory for your project and install all dependencies.

[block:image]
{
  "images": [
    {
      "image": [
        "https://files.readme.io/e19cbee-npx-create-miro-app.png",
        "",
        ""
      ],
      "align": "center"
    }
  ]
}
[/block]

6. Open the project root directory inside your integrated development environment (IDE). The app code includes four particularly relevant files:
   1. `index.html` is the landing page that users see upon installing your app.
   2. `index.js` contains the code that runs when users click the app icon on the app toolbar after the app authorization.
   3. `app.html` contains the app UI and loads the app.js JavaScript code.
   4. `app.js` includes the script for the panel and contains most of the code for this project.

In the Hello, World example, `index.js` pulls the `app.html` file. The `app.html` file is the user interface to the app when the app is running on the board. This is where you can include more UI controls and scripts to enable users to perform further actions within the app, such as creating board items or dragging and dropping items on the board.

The file flow in this example is as follows:

* Start server to run the app on localhost →
* displays `index.html` →
* loads `index.js`	→
* fetches `app.html` →
* loads `app.js`.

[block:image]
{
  "images": [
    {
      "image": [
        "https://files.readme.io/fad27c8-image.png",
        null,
        "Figure 3. IDE showing the code with the app logic."
      ],
      "align": "center",
      "border": true,
      "caption": "Figure 3. IDE showing the code with the app logic."
    }
  ]
}
[/block]

7. To boot up the project to a server running at `http\://localhost:3000/`, go to the project directory, and then run: `npm run start`
8. In your web browser, go to `http\://localhost:3000/` to see a page like this:

[block:image]
{
  "images": [
    {
      "image": [
        "https://files.readme.io/9b67294-image.png",
        null,
        "Figure 4. Confirmation that the app is running."
      ],
      "align": "center",
      "sizing": "50% ",
      "border": true,
      "caption": "Figure 4. Confirmation that the app is running."
    }
  ]
}
[/block]

9. Select **Create a Developer team**, then proceed to create or select a [Developer team](https://miro.com/app/dashboard/?createDevTeam=1).
10. In **Your apps**, select the checkbox to accept Miro's terms of service and create a new app for this project.

[block:image]
{
  "images": [
    {
      "image": [
        "https://files.readme.io/c08e2c2-image.png",
        null,
        "Figure 5. Your apps are a with a checkbox for accepting terms and the + Create new app button."
      ],
      "align": "center",
      "sizing": "50% ",
      "border": true,
      "caption": "Figure 5. Your apps are a with a checkbox for accepting terms and the + Create new app button."
    }
  ]
}
[/block]

11. In the **+ Create new app** modal, enter an **App Name**, such as “Miro Templates App”, select the team, leave the **Expire user authorization token** option deselected, and click **Create app**.

[block:image]
{
  "images": [
    {
      "image": [
        "https://files.readme.io/9b8c5e5-image.png",
        null,
        "Figure 6. Create new app modal with the Create app button."
      ],
      "align": "center",
      "sizing": "50% ",
      "border": true,
      "caption": "Figure 6. Create new app modal with the Create app button."
    }
  ]
}
[/block]

12. Next, scroll down to **App URL** and set it to `http\://localhost:3000/`.
13. Click **Save**.

[block:image]
{
  "images": [
    {
      "image": [
        "https://files.readme.io/4364c90-image.png",
        null,
        "Figure 7. App URL field with the Save button."
      ],
      "align": "center",
      "sizing": "50% ",
      "border": true,
      "caption": "Figure 7. App URL field with the Save button."
    }
  ]
}
[/block]

14. To assign the app read and write access to the board and read access to the current user details, select the following permissions:
    1. **boards:read**
    2. **boards:write**
    3. **identity:read**

[block:image]
{
  "images": [
    {
      "image": [
        "https://files.readme.io/ced8e9e-image.png",
        null,
        "Figure 8. Profile settings page, Permissions section."
      ],
      "align": "center",
      "sizing": "50% ",
      "border": true,
      "caption": "Figure 8. Profile settings page, Permissions section."
    }
  ]
}
[/block]

15. Click **Install app and get OAuth token**, and then select a team to test this app and complete the installation. The app doesn’t require an access token, so click **Close** when done.

[block:image]
{
  "images": [
    {
      "image": [
        "https://files.readme.io/c7d83f9-image.png",
        null,
        "Figure 9. Install app to get OAuth token modal with Install & authorize button."
      ],
      "align": "center",
      "sizing": "50% ",
      "border": true,
      "caption": "Figure 9. Install app to get OAuth token modal with Install & authorize button."
    }
  ]
}
[/block]

## Test the Miro Node.js app

Now, you can test the app on a Miro board to see if everything is working correctly. To test your app:

1. Open your [Dashboard](https://miro.com/app/dashboard), click the Dev Team (DT) icon, and create a new board.
2. Expand the **Apps** panel by clicking **>> more apps** on the app toolbar.
3. Select your app.

[block:image]
{
  "images": [
    {
      "image": [
        "https://files.readme.io/1b4223f-image.png",
        null,
        "Figure 10. App options."
      ],
      "align": "center",
      "sizing": "50% ",
      "border": true,
      "caption": "Figure 10. App options."
    }
  ]
}
[/block]

4. To open the Hello, World example on a sticky note, click the app icon.

[block:image]
{
  "images": [
    {
      "image": [
        "https://files.readme.io/01292c8-image.png",
        null,
        "Figure 11. Board with Hello, World! Sticky note."
      ],
      "align": "center",
      "sizing": "50% ",
      "border": true,
      "caption": "Figure 11. Board with Hello, World! Sticky note."
    }
  ]
}
[/block]

## Set up a Firebase backend with a database

Next, create the database to store the app’s templates.

1. First, open the [Firebase console](https://console.firebase.google.com/?authuser=0) and create a new project. Use the **Enter your project name** field to give it a name, such as “Miro-Templates.”

[block:image]
{
  "images": [
    {
      "image": [
        "https://files.readme.io/d9fdd33-image.png",
        null,
        "Figure 12. Create a project, step 1. Name your project in the Enter your project name field."
      ],
      "align": "center",
      "sizing": "50% ",
      "border": true,
      "caption": "Figure 12. Create a project, step 1. Name your project in the Enter your project name field."
    }
  ]
}
[/block]

2. Optionally, you can enable Google Analytics for the project.
3. When your new project is ready, click Continue.

[block:image]
{
  "images": [
    {
      "image": [
        "https://files.readme.io/f220eb8-image.png",
        null,
        "Figure 15. Your new project is ready notice."
      ],
      "align": "center",
      "sizing": "50% ",
      "border": true,
      "caption": "Figure 15. Your new project is ready notice."
    }
  ]
}
[/block]

4. To store and sync data in real-time, click **Build** to expand it and select **Realtime Database**.
5. Click **Create Database**.

[block:image]
{
  "images": [
    {
      "image": [
        "https://files.readme.io/977dd70-image.png",
        null,
        "Figure 16. Project Overview showing the build options, including Realtime Database."
      ],
      "align": "center",
      "sizing": "75% ",
      "border": true,
      "caption": "Figure 16. Project Overview showing the build options, including Realtime Database."
    }
  ]
}
[/block]

6. Select the **Realtime Database location**, then click **Next**.

[block:image]
{
  "images": [
    {
      "image": [
        "https://files.readme.io/3fa875e-image.png",
        null,
        "Figure 17. Set up database modal with Realtime Database location list."
      ],
      "align": "center",
      "sizing": "75% ",
      "border": true,
      "caption": "Figure 17. Set up database modal with Realtime Database location list."
    }
  ]
}
[/block]

7. Select **Start in test mode**, then click **Enable**.

[block:image]
{
  "images": [
    {
      "image": [
        "https://files.readme.io/5430863-image.png",
        null,
        "Figure 18. Set up database modal showing database start options."
      ],
      "align": "center",
      "sizing": "5px",
      "border": true,
      "caption": "Figure 18. Set up database modal showing database start options."
    }
  ]
}
[/block]

[block:image]
{
  "images": [
    {
      "image": [
        "https://files.readme.io/84800f9-image.png",
        null,
        "Figure 19. Miro-Templates on Firebase."
      ],
      "align": "center",
      "sizing": "75% ",
      "border": true,
      "caption": "Figure 19. Miro-Templates on Firebase."
    }
  ]
}
[/block]

8. Return to the **Project Overview** page and add an app to the project by selecting the **Web (\</>)** option.

[block:image]
{
  "images": [
    {
      "image": [
        "https://files.readme.io/4690355-image.png",
        null,
        "Figure 20. The Web (\\</>) option"
      ],
      "align": "center",
      "sizing": "75% ",
      "border": true,
      "caption": "Figure 20. The Web (\\</>) option"
    }
  ]
}
[/block]

9. Enter an **App nickname**.
10. Leave **Also set up Firebase Hosting for this app** deselected.
11. Click **Register app**.

[block:image]
{
  "images": [
    {
      "image": [
        "https://files.readme.io/bae6be5-image.png",
        null,
        "Figure 21. Add Firebase to your web app page with the App nickname field."
      ],
      "align": "center",
      "sizing": "75% ",
      "border": true,
      "caption": "Figure 21. Add Firebase to your web app page with the App nickname field."
    }
  ]
}
[/block]

12. Finally, select the **Use a script tag** option.

[block:image]
{
  "images": [
    {
      "image": [
        "https://files.readme.io/390f53e-image.png",
        null,
        "Figure 22. Add Firebase to your web app page showing script options."
      ],
      "align": "center",
      "sizing": "75% ",
      "border": true,
      "caption": "Figure 22. Add Firebase to your web app page showing script options."
    }
  ]
}
[/block]

13. Copy and save the `firebaseConfig` section of the script. You need this for the Miro app code.

[block:image]
{
  "images": [
    {
      "image": [
        "https://files.readme.io/90f79e9-image.png",
        null,
        "Figure 23. Add Firebase to your web app page showing Firebase configuration."
      ],
      "align": "center",
      "sizing": "75% ",
      "border": true,
      "caption": "Figure 23. Add Firebase to your web app page showing Firebase configuration."
    }
  ]
}
[/block]

## Access your database from your Miro app

Follow these steps to access your database from your Miro app:

1. Return to your Miro App project code in your IDE.
2. At the very top of the `app.js` file, add the code below.
3. Replace the placeholders in the `firebaseConfig` object with the actual configuration values from your Firebase script:

```coffeescript
import { initializeApp } from "https://www.gstatic.com/firebasejs/9.9.1/firebase-app.js";
import { getDatabase, ref, set, get } from "https://www.gstatic.com/firebasejs/9.9.1/firebase-database.js";

const firebaseConfig = {
  apiKey: "YOUR-API-KEY",
  authDomain: "YOUR-DOMAIN.firebaseapp.com",
  databaseURL: "https://YOUR-DOMAIN-rtdb.firebaseio.com",
  projectId: "YOUR-PROJECT-ID",
  storageBucket: "YOUR-BUCKET.appspot.com",
  messagingSenderId: "YOUR-SENDER-ID",
  appId: "YOUR-APP-ID"
};

// Initialize Firebase
const app = initializeApp(firebaseConfig);
const db = getDatabase( app );

```

4. To read from and write to the Firebase database, add `getTemplatesFromDB` and `saveTemplatesToDB` helper functions. These functions store the templates for each user using their ID:

```coffeescript
async function getTemplatesFromDB( userId ) {
  const data = await get( ref( db, "templates/" + userId ) );
  if( data.exists() ) {
    return data.val();
  }
  return [];
}

function saveTemplatesToDB( userId, templates ) {
  set( ref( db, "templates/" + userId ), templates );
}

```

## Interact with Miro items

To complete the project, you need to save selected board items as templates.

1. To do this, add an element to the app panel to show the saved templates. On the app.html file, add a button inside the div class="grid wrapper" element. This element must also contain a text input field to enable search, and a div id="templates" to display the list of templates:

```coffeescript
<div class="grid wrapper">
  <div class="cs1 ce12">
    <button id="save-btn" class="button button-primary">
      Save selection as template
    </button>
    <div class="form-group">
      <input id="search-bar" class="input" type="text" placeholder="Search templates"/>
    </div>
    <p>Drag templates to the board</p>
    <div id="templates"></div>
  </div>
</div>
```

Then, to maintain the templates locally:

2. Go back to the `app.js` file.
3. Add a `templates` array.
4. To save and delete the templates in the Firebase database, add `saveTemplate` and `deleteTemplate` handler functions under `getTemplatesFromDB` and `saveTemplatesToDB`.
5. For each saved template, the app stores its name, the selected items, and the relative center coordinates of the collection for use when dragging a template onto the board.

```coffeescript
let templates = [];
let filter = "";

window.saveTemplate = ( userId, name, items ) => {
  // Calculate center of the selection
  let totalX = 0, totalY = 0;
  items.forEach( item => {
    totalX += item.x;
    totalY += item.y;
  });
  const center = { x: totalX / items.length, y: totalY / items.length };

  // Save to the Firebase database
  templates.push( { name, items, center });
  saveTemplatesToDB( 
    JSON.parse( JSON.stringify(userId)), 
    JSON.parse( JSON.stringify(templates)) 
  );

  displayTemplates( userId, templates );
}

window.deleteTemplate = ( userId, index ) => {
  templates.splice( index, 1 );
  saveTemplatesToDB( userId, templates );
  displayTemplates( userId, templates );
}
```

6. Now, create a function to clear and show the current templates in the Miro app panel, filtering out any templates that do not match the search filter text. Each template needs a clickable option to enable removing it from the list.

```coffeescript
function displayTemplates( userId, templates ) {
  const templateList = document.getElementById( "templates" );
  templateList.innerHTML = "";
  templates.forEach( ( t, i ) => {
    if( !t.name.toLowerCase().includes( filter.toLowerCase() ) ) { return; }
    templateList.innerHTML += 
      `<div class="cs1 ce12 miro-draggable" data-index="${i}" style="padding: 5px; margin: 5px; border: 5px solid black;">
        <p><strong>${t.name}&nbsp;<span onclick="deleteTemplate('${userId}', ${i})">[X]</span></strong></p>
      </div>`;
  });
}
```

Your app needs one more utility function to:

* Take a JSON object of the template.
* Call the corresponding Miro Web SDK function to create the item and position it on the board by specifying X and Y coordinates.

7. Add the following function, which you can update later to support more item types:

```coffeescript
async function createWidgetFromJson( item, x, y ) {
  const clone = Object.assign( {}, item, { x: item.x + x, y: item.y + y } );
  switch( clone.type ) {
    case "card":
      await miro.board.createCard( clone );
      break;
    case "frame":
      await miro.board.createFrame( clone );
      break;
    case "shape":
      await miro.board.createShape( clone );
      break;
    case "sticky_note":
      await miro.board.createStickyNote( clone );
      break;
    case "text":
      await miro.board.createText( clone );
      break;
  }
}
```

Finally, to create the items from the template inside the init function that we’re about to use, the app must:

* Retrieve the current user information.
* Load the corresponding templates from the Firebase database.
* Assign the save button handler.
* Add an input handler for the search bar.
* Add a drag-and-drop Miro Board UI event handler.

8. To achieve this, add the code below to your `app.js` file. To use the selected items as templates, we need to strip some item properties before saving them.

```coffeescript
async function init() {
  // Get current user info
  const user = await miro.board.getUserInfo();
  
  // Load templates from the database
  templates = await getTemplatesFromDB( user.id );
  displayTemplates( user.id, templates );
  
  // Save button handler
  document.getElementById( "save-btn" ).onclick = async () => {
    const name = prompt( "Please enter a name for the template" );
    // Save items
    const items = await miro.board.getSelection();
    // Remove unnecessary properties
    items.forEach( item => {
      delete item.id;
      delete item.parentId;
      delete item.height; // Note: only save the width and not the height
      delete item.createdAt;
      delete item.createdBy;
      delete item.modifiedAt;
      delete item.modifiedBy;
      delete item.assignee;
      delete item.dueDate;
    });
    saveTemplate( user.id, name, items );
  };

  // Search handler
  document.getElementById( "search-bar" ).addEventListener( "input", (e) => {
    filter = e.target.value;
    displayTemplates( user.id, templates );
  });

  // Drag-and-drop handler
  miro.board.ui.on( "drop", async ({ x, y, target }) => {
    const index = target.getAttribute( "data-index" );
    const template = templates[ index ];
    template.items.forEach( item => {
      createWidgetFromJson( item, x - template.center.x, y - template.center.y );
    });
  });
}

init()
```

## Test the Miro app

1. Return to the board and add some items to your app, such as sticky notes, shapes, or text.

[block:image]
{
  "images": [
    {
      "image": [
        "https://files.readme.io/82964ac-image.png",
        null,
        "Figure 24. Board with some shapes and a sticky note."
      ],
      "align": "center",
      "sizing": "75% ",
      "border": true,
      "caption": "Figure 24. Board with some shapes and a sticky note."
    }
  ]
}
[/block]

2. Open the app, select an item on the board, click **Save selection as template**, and enter a name for the template.

[block:image]
{
  "images": [
    {
      "image": [
        "https://files.readme.io/6c10d04-image.png",
        null,
        "Figure 25. Board showing a selected item and the Save selection as template button."
      ],
      "align": "center",
      "sizing": "75% ",
      "border": true,
      "caption": "Figure 25. Board showing a selected item and the Save selection as template button."
    }
  ]
}
[/block]

[block:image]
{
  "images": [
    {
      "image": [
        "https://files.readme.io/d5a77f5-image.png",
        null,
        "Figure 26. Enter a name for the template field."
      ],
      "align": "center",
      "sizing": "75% ",
      "border": true,
      "caption": "Figure 26. Enter a name for the template field."
    }
  ]
}
[/block]

[block:image]
{
  "images": [
    {
      "image": [
        "https://files.readme.io/f022234-image.png",
        null,
        "Figure 27. The result of clicking the Save selection as template button."
      ],
      "align": "center",
      "sizing": "75% ",
      "border": true,
      "caption": "Figure 27. The result of clicking the Save selection as template button."
    }
  ]
}
[/block]

3. Try selecting multiple widgets and saving more templates.

[block:image]
{
  "images": [
    {
      "image": [
        "https://files.readme.io/1d043bb-image.png",
        null,
        "Figure 28. Multiple templates are named for their widgets."
      ],
      "align": "center",
      "sizing": "75% ",
      "border": true,
      "caption": "Figure 28. Multiple templates are named for their widgets."
    }
  ]
}
[/block]

4. To easily create the items, drag and drop a saved template to the board.
5. Since the stored templates include the user ID, the same user can reuse them on multiple boards.
6. To delete a template, click the corresponding **x** icon.

[block:image]
{
  "images": [
    {
      "image": [
        "https://files.readme.io/f31425a-image.png",
        null,
        "Figure 29. Deleting a template."
      ],
      "align": "center",
      "sizing": "75% ",
      "border": true,
      "caption": "Figure 29. Deleting a template."
    }
  ]
}
[/block]

7. To view the templates stored as JSON objects, inspect your Firebase Realtime Database.

## What's next?

You learned how  to create a Miro app that uses the Miro Web SDK with a separate backend database. The Web SDK enables your team to use Miro boards more easily and efficiently.

To explore what else you can build using Miro, check out the [Web SDK](https://developers.miro.com/docs/web-sdk-reference) and [REST API](https://developers.miro.com/reference/api-reference) documentation.

# Sibling pages

* [Use the browser developer tools with the Miro Web SDK](https://developers.miro.com/miro-ea/docs/use-the-developer-tools-with-the-miro-web-sdk.md)
* [Update and sync item properties](https://developers.miro.com/miro-ea/docs/update-and-sync-item-properties.md)
* [Add icon click to your app](https://developers.miro.com/miro-ea/docs/add-icon-click-to-your-app.md)
* [Add an icon to your app](https://developers.miro.com/miro-ea/docs/add-a-logo-to-your-app.md)
* [Add drag and drop to your app](https://developers.miro.com/miro-ea/docs/add-drag-and-drop-to-your-app.md)
* [Add permission scopes to your app](https://developers.miro.com/miro-ea/docs/add-permission-scopes-to-your-app.md)
* [Add search and filter to your app](https://developers.miro.com/miro-ea/docs/adding-search-and-filter-to-an-sdk-app.md)
* [Build a calendar app in Miro](https://developers.miro.com/miro-ea/docs/building-a-calendar-app-in-miro.md)
* [Convert sticky notes to shapes](https://developers.miro.com/miro-ea/docs/converting-sticky-notes-to-shapes.md)
* [Create mind map from CSV](https://developers.miro.com/miro-ea/docs/create-mind-map-from-csv.md)
* [Add custom actions to your app](https://developers.miro.com/miro-ea/docs/add-custom-actions-to-your-app.md)
* [Deploy a Miro app](https://developers.miro.com/miro-ea/docs/deploy-a-miro-app.md)
* [Log in to a third-party service using OAuth](https://developers.miro.com/miro-ea/docs/log-in-to-a-third-party-service-using-oauth.md)
* [Enable 2-way sync between app cards and GitHub cards](https://developers.miro.com/miro-ea/docs/enable-2-way-sync-between-app-cards-and-github-cards.md)
* [Enable REST API authentication from Miro's Web SDK authorization](https://developers.miro.com/miro-ea/docs/enable_api_authentication_from_sdk_authorization.md)
* [Integrate a digital asset manager in Miro](https://developers.miro.com/miro-ea/docs/integrate-a-digital-asset-manager-in-miro.md)
* [Convert frame coordinates to board coordinates](https://developers.miro.com/miro-ea/docs/frame-coordinates-to-board-coordinates.md)
* [SVG app icon optimization guidelines](https://developers.miro.com/miro-ea/docs/svg-app-icon-optimization.md)
* [Upload images and SVGs as base64-encoded strings](https://developers.miro.com/miro-ea/docs/upload-images-and-svgs-as-base64-encoded-strings.md)
* [Video: Deploy a Next.js App on AWS Amplify](https://developers.miro.com/miro-ea/docs/video-deploy-a-nextjs-app-on-aws-amplify.md)
* [Video: Integrate OpenAI + Miro](https://developers.miro.com/miro-ea/docs/video-integrate-openai-miro.md)
* [Build collaborative app experiences](https://developers.miro.com/miro-ea/docs/build-collaborative-app-experiences.md)
* [Display banner/modal on a Miro board using DynamoDB](https://developers.miro.com/miro-ea/docs/display-bannermodal-on-a-miro-board-using-dynamodb.md)
* [Display banner/modal on a Miro board using AWS S3](https://developers.miro.com/miro-ea/docs/display-bannermodal-on-a-miro-board-using-aws-s3.md)