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:
- ✅ Cursor tracking for real-time collaboration (overlay captures mouse events)
- ✅ 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:
<!-- 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:
| Mode | Overlay State | User Can | Cursor Sync | Use Case |
|---|---|---|---|---|
| Preview Mode | pointer-events: auto | Navigate canvas, see component | ✅ Active | Moving around board |
| Interaction Mode | pointer-events: none | Fully interact with iframe | ⚠️ Paused | Working 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:
- Removes the overlay (
pointer-events: none) - Shows a blue border around the component
- Displays a "Done" button in the top-right corner
- 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:
- Click "Done" button (top-right corner)
- Press ESC key
- 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!
<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:
- Don't rely on immediate hover/click - users must double-click to interact
- Test in iframe - your component will always run in an iframe
- Use BoardAPI SDK events - emit events when users interact
Example component:
<!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:
- User sees calculator in Preview Mode (can't interact yet)
- User double-clicks → enters Interaction Mode
- User enters numbers, clicks "Calculate"
- Component emits event via SDK
- 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:
{
"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:
// 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:
<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
contentChangedevents 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:
// 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:
- Verify component is using
type: "external-component" - Check iframe sandbox attributes allow interaction
- Confirm component is loaded (check browser console)
- Test double-click to enter Interaction Mode
Issue: Component doesn't load at all
Symptoms:
- Blank iframe
- CORS errors in console
Solution:
- Check
componentUrlis accessible - Verify CORS headers on your component hosting
- Ensure manifest.json is valid
- 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-actionrules - 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
/* 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
<!-- Custom button -->
<button class="exit-interaction" @click="exitInteractionMode">
<Icon name="x" /> Close
</button>Custom Keyboard Shortcuts
// Custom shortcut (e.g., Shift+ESC)
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && e.shiftKey) {
exitInteractionMode()
}
})Related Documentation
- External Components SDK - Build interactive components
- SDK API Reference - All SDK methods and events
- WebSocket Events - Real-time collaboration events
- Custom Objects vs Components - Choose the right approach
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:
- Users must explicitly enter Interaction Mode (double-click)
- Cursor sync pauses during interaction (expected behavior)
- Clear visual feedback (border, button, hint)
- 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!