Skip to content

Interaction Modes for Embedded Components

When embedding interactive content (iframes, External Components, or embedded apps) in collaborative whiteboards, you face a fundamental browser limitation. This guide explains the problem and how BoardAPI solves it with Interaction Modes.

The Infinite Canvas Challenge

The Problem

You cannot simultaneously have:

  1. ✅ Cursor tracking for real-time collaboration (overlay captures mouse events)
  2. ✅ Full native interaction with iframe content (clicks, scroll, text selection)

Why? It's a browser security limitation:

  • If overlay has pointer-events: auto → cursor tracking works ✅, but iframe is blocked ❌
  • If overlay has pointer-events: none → iframe works ✅, but cursor tracking stops ❌

This affects any collaborative whiteboard with embedded content - Miro, Figma, Excalidraw, and BoardAPI.

Example Scenario

Imagine you embed a YouTube video on your board:

html
<!-- Embedded iframe -->
<iframe src="https://youtube.com/embed/..."></iframe>

<!-- Overlay for cursor tracking -->
<div class="cursor-tracker"></div>

Without Interaction Mode:

  • Users can see the video, but can't click play/pause
  • Trying to scroll the video's comments scrolls the whole board instead
  • Other collaborators can't see your cursor when you're hovering the video

Solution: Interaction Mode Pattern

BoardAPI implements the industry-standard Interaction Mode pattern (same as Miro, Figma).

Concept

Two distinct modes with explicit user control:

ModeOverlay StateUser CanCursor SyncUse Case
Preview Modepointer-events: autoNavigate canvas, see component✅ ActiveMoving around board
Interaction Modepointer-events: noneFully interact with iframe⚠️ PausedWorking with component

User Journey

┌─────────────────────────────────────────────────────────────┐
│ 1. PREVIEW MODE (Default)                                   │
│    ┌─────────────────────────────────────────┐             │
│    │ [External Component]                    │             │
│    │  ✅ Cursor tracking active              │             │
│    │  ❌ Iframe interaction blocked          │             │
│    │                                          │             │
│    │  Hover → "Double-click to interact"     │             │
│    └─────────────────────────────────────────┘             │
└─────────────────────────────────────────────────────────────┘
                           ↓ Double-click
┌─────────────────────────────────────────────────────────────┐
│ 2. INTERACTION MODE                                          │
│    ┌─────────────────────────────────────────┐ ┌────────┐  │
│    │ [External Component] ← Blue Border      │ │ Done ✓ │  │
│    │  ⚠️ Cursor tracking paused              │ └────────┘  │
│    │  ✅ Iframe interaction enabled          │             │
│    │                                          │             │
│    │  Click, scroll, hover, type → all work! │             │
│    └─────────────────────────────────────────┘             │
└─────────────────────────────────────────────────────────────┘
                           ↓ Click "Done" / ESC / Click outside
┌─────────────────────────────────────────────────────────────┐
│ 3. Back to PREVIEW MODE                                      │
│    ✅ Cursor tracking restored                              │
└─────────────────────────────────────────────────────────────┘

How to Use Interaction Modes

Entering Interaction Mode

Primary method: Double-click the component

When you double-click an External Component, BoardAPI:

  1. Removes the overlay (pointer-events: none)
  2. Shows a blue border around the component
  3. Displays a "Done" button in the top-right corner
  4. Pauses cursor sync for this component

Now you can fully interact with the iframe - click buttons, scroll, select text, etc.

Exiting Interaction Mode

Three ways to exit:

  1. Click "Done" button (top-right corner)
  2. Press ESC key
  3. Click anywhere outside the component (on the canvas)

After exiting, cursor tracking resumes immediately.


Visual Feedback

Preview Mode (Hover)

When you hover over a component in Preview Mode:

  • Subtle blue tint overlay
  • Tooltip: "Double-click to interact"
  • Pointer cursor
  • Your cursor is visible to other collaborators

Interaction Mode (Active)

When you enter Interaction Mode:

  • Blue border around the component
  • Glow effect (soft shadow)
  • "Done" button in top-right corner
  • Normal cursor (component controls cursor style)
  • Your cursor position is "frozen" for other collaborators (they see where you entered Interaction Mode)

Integration Guide

Using BoardAPI SDK in Your App

If you're embedding BoardAPI boards in your application, External Components automatically support Interaction Modes - no extra code needed!

html
<iframe src="https://app.boardapi.io/boards/{boardId}"></iframe>

All External Components on the board will have Interaction Mode enabled by default.

Creating External Components

When developing your own External Components, design them assuming they'll run in Interaction Mode:

Best practices:

  1. Don't rely on immediate hover/click - users must double-click to interact
  2. Test in iframe - your component will always run in an iframe
  3. Use BoardAPI SDK events - emit events when users interact

Example component:

html
<!DOCTYPE html>
<html>
<head>
  <style>
    /* Your component styles */
    .interactive-widget {
      padding: 20px;
      background: white;
      border-radius: 8px;
    }
    button {
      padding: 10px 20px;
      cursor: pointer;
    }
  </style>
</head>
<body>
  <div class="interactive-widget">
    <h3>Interactive Calculator</h3>
    <input type="number" id="num1" placeholder="Number 1">
    <input type="number" id="num2" placeholder="Number 2">
    <button id="calculate">Calculate</button>
    <p>Result: <span id="result">-</span></p>
  </div>

  <script src="https://cdn.boardapi.io/sdk/v2.4.0/board-sdk.min.js"></script>
  <script>
    const board = new BoardSDK()

    board.onReady(() => {
      document.getElementById('calculate').addEventListener('click', () => {
        const num1 = parseFloat(document.getElementById('num1').value)
        const num2 = parseFloat(document.getElementById('num2').value)
        const result = num1 + num2

        document.getElementById('result').textContent = result

        // Notify board about interaction
        board.emitEvent('calculated', { num1, num2, result })
      })
    })
  </script>
</body>
</html>

User interaction flow:

  1. User sees calculator in Preview Mode (can't interact yet)
  2. User double-clicks → enters Interaction Mode
  3. User enters numbers, clicks "Calculate"
  4. Component emits event via SDK
  5. User clicks "Done" or ESC → exits Interaction Mode

Common Use Cases

1. Embedded Video Players

Example: YouTube, Vimeo, custom video players

Why Interaction Mode helps:

  • Users can play/pause, seek, adjust volume
  • Video controls remain fully functional
  • Comments/description are scrollable

Integration:

json
{
  "type": "external-component",
  "data": {
    "componentUrl": "https://app.boardapi.io/components/video-player@1.0.0",
    "props": {
      "videoUrl": "https://youtube.com/embed/dQw4w9WgXcQ"
    }
  }
}

2. Interactive Forms

Example: Surveys, registration forms, quizzes

Why Interaction Mode helps:

  • Users can type in text fields
  • Dropdowns and checkboxes work natively
  • Form validation behaves normally

Best practice:

javascript
// In your component
board.onReady(() => {
  const form = document.getElementById('surveyForm')

  form.addEventListener('submit', (e) => {
    e.preventDefault()
    const formData = new FormData(form)

    // Send data via SDK
    board.emitEvent('formSubmitted', Object.fromEntries(formData))

    // Optionally auto-exit Interaction Mode
    board.requestExit() // Custom helper (if implemented)
  })
})

3. Data Visualizations

Example: Charts, graphs, interactive dashboards

Why Interaction Mode helps:

  • Users can hover to see tooltips
  • Zoom/pan controls work
  • Click legends to toggle data series

Example:

html
<canvas id="chart"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
  const board = new BoardSDK()

  board.onReady(() => {
    const props = board.getProps()

    new Chart(document.getElementById('chart'), {
      type: 'line',
      data: props.chartData,
      options: {
        interaction: {
          mode: 'index',
          intersect: false
        },
        plugins: {
          tooltip: {
            enabled: true // Works in Interaction Mode!
          }
        }
      }
    })
  })
</script>

4. Collaborative Text Editors

Example: Rich text editors, code editors, notes

Why Interaction Mode helps:

  • Full keyboard input support
  • Text selection works natively
  • Formatting toolbar is clickable

Consider:

  • Use SDK events to sync content changes
  • Emit contentChanged events on blur or debounced
  • Store content in board state for persistence

Cursor Sync Trade-Off

Why Cursor Sync Pauses

When a user enters Interaction Mode, their cursor position is "frozen" for other collaborators. This is intentional and expected.

Reasoning:

  • User is focused on iframe content (filling form, watching video)
  • They're not navigating the board canvas
  • Freezing cursor position is better than showing incorrect position
  • When they exit, cursor sync resumes immediately

What Other Collaborators See

Before Interaction Mode:

👤 Alice (cursor moving around board)

Alice enters Interaction Mode on a calculator widget:

👤 Alice (cursor locked at calculator position)
   └─ Status: "Interacting with component"

Alice exits Interaction Mode:

👤 Alice (cursor moving normally again)

Implementation Note

If you need cursor tracking inside the component (e.g., for collaborative editing), you can implement it via SDK:

javascript
// Inside your component
document.addEventListener('mousemove', (e) => {
  board.emitEvent('cursorMove', {
    x: e.clientX,
    y: e.clientY,
    componentId: board.getComponentId()
  })
})

Then handle this event on the board level to show cursors inside the iframe.


Best Practices

1. Design for Preview Mode First

Assume users will see your component in Preview Mode initially:

Do:

  • Show clear UI even in Preview (not blank)
  • Display static content that makes sense without interaction
  • Use visual cues (play button icon for video players)

Don't:

  • Show loading states forever
  • Require interaction to see anything
  • Display error messages in Preview

2. Clear Call-to-Action

Help users understand they need to double-click:

Do:

  • Add hint text: "Double-click to interact"
  • Use semi-transparent overlay with icon
  • Show cursor pointer on hover

Don't:

  • Assume users know about Interaction Mode
  • Hide important content behind interaction

3. Graceful Exit

Make it easy to exit Interaction Mode:

Do:

  • Emit events when user completes action (form submit, quiz finish)
  • Show visual confirmation before exit (e.g., "Quiz submitted!")
  • Respect ESC key and outside clicks

Don't:

  • Trap users in Interaction Mode
  • Prevent ESC key from working
  • Block clicks outside component

4. Performance

Optimize for iframe rendering:

Do:

  • Lazy-load heavy assets
  • Debounce event emissions
  • Use requestAnimationFrame for animations

Don't:

  • Load huge libraries on mount
  • Emit events on every mousemove
  • Use heavy polling

Troubleshooting

Issue: Users can't interact with my component

Symptoms:

  • Clicks are ignored
  • Scroll doesn't work
  • Hover effects don't trigger

Solution:

  1. Verify component is using type: "external-component"
  2. Check iframe sandbox attributes allow interaction
  3. Confirm component is loaded (check browser console)
  4. Test double-click to enter Interaction Mode

Issue: Component doesn't load at all

Symptoms:

  • Blank iframe
  • CORS errors in console

Solution:

  1. Check componentUrl is accessible
  2. Verify CORS headers on your component hosting
  3. Ensure manifest.json is valid
  4. Check browser console for errors

Issue: Cursor sync doesn't resume after exit

Symptoms:

  • After exiting Interaction Mode, cursor position is stuck

Solution:

  • This is a rare bug - refresh the board
  • Report to BoardAPI support with browser/version

Issue: Double-click doesn't work on mobile

Expected behavior:

  • Mobile uses single tap to enter Interaction Mode
  • No double-tap needed

If not working:

  • Check component CSS for touch-action rules
  • Verify iframe is not preventing touch events

Industry Comparison

BoardAPI's Interaction Mode follows proven patterns:

Miro

  • Entry: Double-click embedded app
  • Visual: Blue border + "Done" button
  • Exit: Click "Done", ESC, or outside
  • ✅ Same as BoardAPI!

Figma

  • Entry: Click embedded frame
  • Visual: Blue border highlight
  • Exit: ESC or click outside
  • ✅ Similar to BoardAPI

Notion

  • Entry: Click embedded content
  • Visual: Subtle border
  • Exit: Click outside
  • ✅ Concept matches

Takeaway: Interaction Mode is an industry standard for collaborative tools with embedded content.


Advanced: Custom Interaction Mode UI

If you're white-labeling BoardAPI or building a custom frontend, you can customize Interaction Mode UI:

Custom Border Style

css
/* Your CSS */
.external-component.interaction-mode {
  border: 3px solid var(--brand-color);
  box-shadow: 0 0 0 5px rgba(var(--brand-rgb), 0.3);
}

Custom Exit Button

html
<!-- Custom button -->
<button class="exit-interaction" @click="exitInteractionMode">
  <Icon name="x" /> Close
</button>

Custom Keyboard Shortcuts

javascript
// Custom shortcut (e.g., Shift+ESC)
document.addEventListener('keydown', (e) => {
  if (e.key === 'Escape' && e.shiftKey) {
    exitInteractionMode()
  }
})


Summary

Interaction Modes solve the fundamental browser limitation of overlays vs iframes:

  • Preview Mode (default): Cursor tracking works, iframe blocked
  • Interaction Mode: Iframe works, cursor tracking pauses
  • User controls: Double-click to enter, Done/ESC/outside to exit
  • Industry standard: Same pattern as Miro, Figma, Notion

Key Points:

  1. Users must explicitly enter Interaction Mode (double-click)
  2. Cursor sync pauses during interaction (expected behavior)
  3. Clear visual feedback (border, button, hint)
  4. Multiple exit options (Done, ESC, outside click)

Design your External Components with Interaction Mode in mind, and your users will have a seamless collaborative experience!