Skip to content

board.lifecycle - Component Events

Handle component lifecycle events and state transitions.

Overview

board.lifecycle provides events for:

  • Component initialization
  • Focus/blur (Interaction Mode)
  • Visibility changes (viewport scrolling)
  • Reconnection after network loss
  • Component destruction

Core Events

ready - Component Initialized

Fired when the component is fully loaded and SDK is ready:

typescript
board.on('ready', () => {
  console.log('Component initialized!');

  // Safe to access all SDK features
  const count = board.storage.get('count', 0);
  renderUI(count);
});

focus - Enter Interaction Mode

Fired when user clicks on the component (iframe becomes active):

typescript
board.on('focus', () => {
  console.log('User started interacting');

  // Start animations, enable input
  startAnimations();
  enableInput();
  playBackgroundMusic();
});

blur - Exit Interaction Mode

Fired when user clicks outside the component (presses ESC):

typescript
board.on('blur', () => {
  console.log('User stopped interacting');

  // SDK automatically generates preview
  // You can also generate custom preview
  pauseAnimations();
  disableInput();
  stopBackgroundMusic();
});

beforeFreeze - Before Preview Generation

Fired just before the component is "frozen" (before blur):

typescript
board.on('beforeFreeze', async () => {
  // Generate custom preview
  const svg = await generateCustomPreview();
  await board.ui.updatePreview(svg);
});

destroy - Component Removed

Fired when the component is removed from the board:

typescript
board.on('destroy', () => {
  console.log('Component being destroyed');

  // Cleanup resources
  clearInterval(timerId);
  audioElement.pause();
  videoElement.pause();
});

Connection Events

reconnect - Connection Restored

Fired when WebSocket reconnects after network loss:

typescript
board.on('reconnect', () => {
  console.log('Connection restored');

  // SDK automatically syncs state
  // You can show a notification
  board.ui.showToast('Connection restored', { type: 'success' });
});

Visibility Events

visibilityChange - Component Scrolled Out

Fired when component leaves/enters viewport:

typescript
board.on('visibilityChange', (isVisible: boolean) => {
  if (!isVisible) {
    // Component scrolled out of view
    // Pause heavy operations
    pauseWebGLAnimation();
    stopVideoPlayback();
    console.log('Component scrolled out of view');
  } else {
    // Component back in view
    resumeWebGLAnimation();
    resumeVideoPlayback();
    console.log('Component back in view');
  }
});

// Get current visibility state
const isVisible = board.lifecycle.isVisible;

Examples

Timer Component

typescript
import { BoardAPI } from '@boardapi/sdk';

const board = new BoardAPI();
let timerId: number;

// Initialize
board.on('ready', () => {
  const duration = board.props.get('duration', 60);
  const autoStart = board.props.get('autoStart', false);

  if (autoStart) {
    startTimer(duration);
  }
});

// Start timer when user interacts
board.on('focus', () => {
  const isRunning = board.storage.get('isRunning');
  if (isRunning) {
    resumeTimer();
  }
});

// Pause timer when user leaves
board.on('blur', () => {
  pauseTimer();
});

// Cleanup on destroy
board.on('destroy', () => {
  clearInterval(timerId);
});

function startTimer(duration: number) {
  timerId = setInterval(() => {
    board.storage.update('remaining', (prev) => Math.max(0, (prev || duration) - 1));
  }, 1000);

  board.storage.set('isRunning', true);
}

function pauseTimer() {
  clearInterval(timerId);
  board.storage.set('isRunning', false);
}

function resumeTimer() {
  const remaining = board.storage.get('remaining');
  startTimer(remaining);
}

Video Player Component

typescript
const board = new BoardAPI();
const video = document.querySelector<HTMLVideoElement>('#video');

// Auto-play on focus
board.on('focus', async () => {
  try {
    await video.play();
  } catch (err) {
    console.error('Autoplay prevented:', err);
  }
});

// Pause on blur
board.on('blur', () => {
  video.pause();
});

// Pause when scrolled out of view
board.on('visibilityChange', (isVisible) => {
  if (!isVisible) {
    video.pause();
  }
});

// Cleanup
board.on('destroy', () => {
  video.pause();
  video.src = '';
});

Canvas Animation

typescript
const board = new BoardAPI();
const canvas = document.querySelector<HTMLCanvasElement>('#canvas');
const ctx = canvas.getContext('2d');
let animationFrame: number;

function animate() {
  // Clear and redraw
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  drawFrame();

  animationFrame = requestAnimationFrame(animate);
}

// Start animation on ready (if visible)
board.on('ready', () => {
  if (board.lifecycle.isVisible) {
    animate();
  }
});

// Resume on focus
board.on('focus', () => {
  if (!animationFrame) {
    animate();
  }
});

// Pause on blur
board.on('blur', () => {
  if (animationFrame) {
    cancelAnimationFrame(animationFrame);
    animationFrame = null;
  }
});

// Pause when scrolled out
board.on('visibilityChange', (isVisible) => {
  if (!isVisible && animationFrame) {
    cancelAnimationFrame(animationFrame);
    animationFrame = null;
  } else if (isVisible && !animationFrame) {
    animate();
  }
});

// Cleanup
board.on('destroy', () => {
  if (animationFrame) {
    cancelAnimationFrame(animationFrame);
  }
});

Auto-Save Form

typescript
const board = new BoardAPI();
let saveTimeout: number;

// Auto-save on input (debounced)
document.querySelector('#form').addEventListener('input', (e) => {
  clearTimeout(saveTimeout);

  saveTimeout = setTimeout(async () => {
    const formData = collectFormData();
    await board.storage.set('formData', formData);

    board.ui.showToast('Saved', { type: 'success', duration: 1000 });
  }, 1000); // Save 1 second after typing stops
});

// Save immediately on blur
board.on('blur', async () => {
  clearTimeout(saveTimeout);
  const formData = collectFormData();
  await board.storage.set('formData', formData);
});

// Cleanup
board.on('destroy', () => {
  clearTimeout(saveTimeout);
});

Reconnection Handler

typescript
const board = new BoardAPI();

// Show offline indicator
board.connection.on('statusChange', (status) => {
  const indicator = document.querySelector('#connection-status');

  if (status === 'offline') {
    indicator.textContent = 'Offline - Changes will sync when reconnected';
    indicator.className = 'offline';
  } else if (status === 'connecting') {
    indicator.textContent = 'Reconnecting...';
    indicator.className = 'connecting';
  } else {
    indicator.textContent = 'Connected';
    indicator.className = 'online';
  }
});

// Sync on reconnect
board.on('reconnect', async () => {
  console.log('Reconnected - syncing state');

  // SDK auto-syncs, but you can also refetch data
  await refetchExternalData();

  board.ui.showToast('Synced', { type: 'success' });
});

Event Order

Typical lifecycle:

1. ready          → Component loaded
2. focus          → User clicked component
   (user interacts)
3. blur           → User pressed ESC / clicked outside
4. beforeFreeze   → About to generate preview
   (component frozen)

   ... later ...

5. focus          → User clicked again
   (repeat)

   ... when removed ...

6. destroy        → Component deleted

With network issues:

1. ready
2. (connection lost)
3. reconnect      → Connection restored

With scrolling:

1. ready
2. visibilityChange(false)  → Scrolled out
3. visibilityChange(true)   → Scrolled back

Best Practices

  1. Always cleanup - Use destroy event to prevent memory leaks
  2. Pause on blur - Save battery, prevent background noise
  3. Use visibility events - Optimize performance when offscreen
  4. Handle reconnection - Show status, don't lose data
  5. Debounce auto-save - Don't spam the server

What's Next?