Files
michaelschiemer/docs/livecomponents/events-reference.md
Michael Schiemer fc3d7e6357 feat(Production): Complete production deployment infrastructure
- Add comprehensive health check system with multiple endpoints
- Add Prometheus metrics endpoint
- Add production logging configurations (5 strategies)
- Add complete deployment documentation suite:
  * QUICKSTART.md - 30-minute deployment guide
  * DEPLOYMENT_CHECKLIST.md - Printable verification checklist
  * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle
  * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference
  * production-logging.md - Logging configuration guide
  * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation
  * README.md - Navigation hub
  * DEPLOYMENT_SUMMARY.md - Executive summary
- Add deployment scripts and automation
- Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment
- Update README with production-ready features

All production infrastructure is now complete and ready for deployment.
2025-10-25 19:18:37 +02:00

35 KiB

LiveComponents Events Reference

Complete reference for all client-side events and lifecycle hooks with examples and best practices.

Table of Contents


Lifecycle Events

livecomponent:init

Fired: When a LiveComponent is discovered in the DOM but before initialization.

Use Case: Prepare component before mounting, modify initial state.

Event Detail:

{
    componentId: string;      // Unique component ID
    element: HTMLElement;     // Component root element
    props: object;           // Initial component props
}

Example:

window.addEventListener('livecomponent:init', (event) => {
    const { componentId, element, props } = event.detail;

    console.log('Initializing component:', componentId);

    // Modify initial props
    if (props.theme === undefined) {
        props.theme = localStorage.getItem('theme') || 'light';
    }

    // Add custom data attributes
    element.dataset.initialized = Date.now();
});

Common Use Cases:

  • Restore state from localStorage
  • Set default props from user preferences
  • Initialize third-party libraries
  • Track component initialization analytics

livecomponent:mounted

Fired: After a LiveComponent is fully mounted and ready for interaction.

Use Case: Initialize JavaScript behaviors, start timers, load external data.

Event Detail:

{
    componentId: string;      // Unique component ID
    element: HTMLElement;     // Component root element
    component: Component;     // Component instance
    props: object;           // Current component props
}

Example:

window.addEventListener('livecomponent:mounted', (event) => {
    const { componentId, element, component } = event.detail;

    // Initialize tooltips
    const tooltips = element.querySelectorAll('[data-tooltip]');
    tooltips.forEach(el => new Tooltip(el));

    // Start auto-refresh timer
    if (component.config.autoRefresh) {
        setInterval(() => {
            component.executeAction('refresh');
        }, component.config.refreshInterval || 30000);
    }

    // Track mount analytics
    analytics.track('component_mounted', {
        component_type: componentId,
        mount_time: Date.now()
    });
});

Common Use Cases:

  • Initialize third-party plugins (charts, maps, editors)
  • Start polling/auto-refresh timers
  • Bind custom event listeners
  • Load deferred content
  • Track analytics

livecomponent:updated

Fired: After a LiveComponent has been updated (re-rendered).

Use Case: React to state changes, update third-party integrations, track updates.

Event Detail:

{
    componentId: string;      // Unique component ID
    element: HTMLElement;     // Component root element
    component: Component;     // Component instance
    previousProps: object;    // Props before update
    currentProps: object;     // Props after update
    changedProps: string[];   // Names of changed props
    fragments: string[];      // Updated fragments (if partial render)
}

Example:

window.addEventListener('livecomponent:updated', (event) => {
    const { componentId, previousProps, currentProps, changedProps, fragments } = event.detail;

    // Re-initialize elements in updated fragments
    if (fragments.includes('chart-container')) {
        const chartEl = document.querySelector('[data-lc-fragment="chart-container"]');
        initializeChart(chartEl, currentProps.chartData);
    }

    // Track specific prop changes
    if (changedProps.includes('status')) {
        console.log('Status changed:', previousProps.status, '→', currentProps.status);

        if (currentProps.status === 'completed') {
            showSuccessNotification();
        }
    }

    // Update document title
    if (changedProps.includes('unreadCount')) {
        document.title = currentProps.unreadCount > 0
            ? `(${currentProps.unreadCount}) Messages`
            : 'Messages';
    }
});

Common Use Cases:

  • Re-initialize third-party components in updated fragments
  • Trigger animations on state changes
  • Update browser title/favicon based on state
  • Track state change analytics
  • Sync with external systems

livecomponent:unmounted

Fired: Before a LiveComponent is removed from the DOM.

Use Case: Cleanup timers, remove event listeners, save state.

Event Detail:

{
    componentId: string;      // Unique component ID
    element: HTMLElement;     // Component root element (about to be removed)
    component: Component;     // Component instance
}

Example:

window.addEventListener('livecomponent:unmounted', (event) => {
    const { componentId, element, component } = event.detail;

    // Save state to localStorage
    localStorage.setItem(`component-${componentId}`, JSON.stringify(component.props));

    // Clear timers
    if (window.componentTimers[componentId]) {
        clearInterval(window.componentTimers[componentId]);
        delete window.componentTimers[componentId];
    }

    // Destroy third-party instances
    if (window.chartInstances[componentId]) {
        window.chartInstances[componentId].destroy();
        delete window.chartInstances[componentId];
    }

    // Track unmount analytics
    analytics.track('component_unmounted', {
        component_type: componentId,
        lifetime_ms: Date.now() - parseInt(element.dataset.initialized)
    });
});

Common Use Cases:

  • Save component state before removal
  • Clear timers and intervals
  • Remove global event listeners
  • Destroy third-party plugin instances
  • Cancel pending network requests
  • Track component lifetime analytics

Action Events

livecomponent:action-start

Fired: When a LiveAction is triggered (before server request).

Use Case: Show loading states, disable buttons, track action starts.

Event Detail:

{
    componentId: string;      // Component ID
    action: string;          // Action name
    params: object;          // Action parameters
    element: HTMLElement;    // Element that triggered action
    timestamp: number;       // Start timestamp
}

Example:

window.addEventListener('livecomponent:action-start', (event) => {
    const { componentId, action, params, element } = event.detail;

    // Show loading state
    element.classList.add('loading');
    element.disabled = true;

    // Show loading spinner for long-running actions
    if (['generateReport', 'processPayment'].includes(action)) {
        showLoadingSpinner(`Processing ${action}...`);
    }

    // Track action starts
    analytics.track('action_started', {
        component: componentId,
        action: action,
        params: params
    });
});

Common Use Cases:

  • Show loading indicators
  • Disable form buttons to prevent double-submission
  • Show progress overlays
  • Track action analytics
  • Optimistic UI updates

livecomponent:action-executed

Fired: After a LiveAction completes successfully.

Use Case: Hide loading states, show success messages, track performance.

Event Detail:

{
    componentId: string;      // Component ID
    action: string;          // Action name
    params: object;          // Action parameters
    result: any;             // Action result
    duration: number;        // Execution time (ms)
    timestamp: number;       // Completion timestamp
}

Example:

window.addEventListener('livecomponent:action-executed', (event) => {
    const { componentId, action, result, duration } = event.detail;

    // Hide loading states
    hideLoadingSpinner();

    // Show success message for specific actions
    if (action === 'saveForm') {
        showToast('Changes saved successfully!', 'success');
    }

    // Track slow actions
    if (duration > 1000) {
        console.warn(`Slow action: ${action} took ${duration}ms`);
        analytics.track('slow_action', {
            component: componentId,
            action: action,
            duration: duration
        });
    }

    // Update badge counts
    if (action === 'markAsRead') {
        const badge = document.querySelector('.unread-badge');
        const count = parseInt(badge.textContent) - 1;
        badge.textContent = count > 0 ? count : '';
    }
});

Common Use Cases:

  • Hide loading indicators
  • Show success notifications
  • Update UI elements outside component
  • Track action performance
  • Trigger follow-up actions

livecomponent:action-failed

Fired: When a LiveAction fails (validation, authorization, or server error).

Use Case: Show error messages, re-enable forms, track failures.

Event Detail:

{
    componentId: string;      // Component ID
    action: string;          // Action name
    params: object;          // Action parameters
    error: {
        message: string;      // Error message
        code: string;        // Error code
        details: object;     // Additional error details
    };
    duration: number;        // Time before failure (ms)
    timestamp: number;       // Failure timestamp
}

Example:

window.addEventListener('livecomponent:action-failed', (event) => {
    const { componentId, action, error, element } = event.detail;

    // Re-enable form elements
    if (element) {
        element.classList.remove('loading');
        element.disabled = false;
    }

    // Show error message
    if (error.code === 'VALIDATION_ERROR') {
        showValidationErrors(error.details);
    } else if (error.code === 'RATE_LIMIT_EXCEEDED') {
        showToast(`Too many requests. Please wait ${error.details.retryAfter} seconds.`, 'warning');
    } else {
        showToast(error.message || 'An error occurred', 'error');
    }

    // Track failures
    analytics.track('action_failed', {
        component: componentId,
        action: action,
        error_code: error.code,
        error_message: error.message
    });
});

Common Use Cases:

  • Display error messages
  • Re-enable disabled form elements
  • Show validation errors
  • Track error rates
  • Trigger error recovery flows

State Events

livecomponent:state-changed

Fired: When component state changes (any LiveProp update).

Use Case: React to any state change, sync with external systems.

Event Detail:

{
    componentId: string;      // Component ID
    previousState: object;    // State before change
    currentState: object;     // State after change
    changedProps: string[];   // Names of changed props
}

Example:

window.addEventListener('livecomponent:state-changed', (event) => {
    const { componentId, previousState, currentState, changedProps } = event.detail;

    // Sync with localStorage
    localStorage.setItem(`state-${componentId}`, JSON.stringify(currentState));

    // Track state changes
    changedProps.forEach(prop => {
        console.log(`${prop} changed:`, previousState[prop], '→', currentState[prop]);
    });

    // Trigger side effects
    if (changedProps.includes('selectedItems')) {
        updateBulkActionBar(currentState.selectedItems);
    }
});

Common Use Cases:

  • Persist state to localStorage
  • Sync with external state management
  • Update derived UI elements
  • Track state change analytics
  • Debug state changes

livecomponent:prop-updated

Fired: When a specific LiveProp is updated.

Use Case: React to specific property changes, trigger dependent updates.

Event Detail:

{
    componentId: string;      // Component ID
    propName: string;        // Updated property name
    previousValue: any;      // Value before update
    currentValue: any;       // Value after update
    source: 'server' | 'client' | 'optimistic';  // Update source
}

Example:

window.addEventListener('livecomponent:prop-updated', (event) => {
    const { componentId, propName, previousValue, currentValue, source } = event.detail;

    // React to specific prop changes
    if (propName === 'theme') {
        document.body.classList.remove(`theme-${previousValue}`);
        document.body.classList.add(`theme-${currentValue}`);
        localStorage.setItem('theme', currentValue);
    }

    if (propName === 'count' && source === 'optimistic') {
        // Animate optimistic updates
        animateNumberChange(previousValue, currentValue);
    }

    if (propName === 'sortOrder') {
        // Re-sort table when sort order changes
        sortTable(currentValue);
    }
});

Common Use Cases:

  • Trigger animations on specific prop changes
  • Update global UI state (theme, locale)
  • Sync specific props with localStorage
  • Track important prop changes
  • Trigger dependent updates

Network Events

livecomponent:request-start

Fired: When a network request starts (action or state sync).

Use Case: Show global loading indicators, track request starts.

Event Detail:

{
    componentId: string;      // Component ID
    requestId: string;       // Unique request ID
    type: 'action' | 'sync'; // Request type
    payload: object;         // Request payload
    timestamp: number;       // Start timestamp
}

Example:

let activeRequests = 0;

window.addEventListener('livecomponent:request-start', (event) => {
    const { componentId, requestId, type } = event.detail;

    activeRequests++;

    // Show global loading indicator
    if (activeRequests === 1) {
        document.body.classList.add('loading');
    }

    // Track request
    console.log(`Request ${requestId} started for ${componentId} (type: ${type})`);
});

livecomponent:request-complete

Fired: When a network request completes successfully.

Use Case: Hide loading indicators, track request performance.

Event Detail:

{
    componentId: string;      // Component ID
    requestId: string;       // Unique request ID
    type: 'action' | 'sync'; // Request type
    response: object;        // Server response
    duration: number;        // Request duration (ms)
    timestamp: number;       // Completion timestamp
}

Example:

window.addEventListener('livecomponent:request-complete', (event) => {
    const { componentId, requestId, duration } = event.detail;

    activeRequests--;

    // Hide global loading indicator
    if (activeRequests === 0) {
        document.body.classList.remove('loading');
    }

    // Track slow requests
    if (duration > 1000) {
        console.warn(`Slow request: ${requestId} took ${duration}ms`);
    }
});

livecomponent:request-failed

Fired: When a network request fails.

Use Case: Handle network errors, retry logic, track failures.

Event Detail:

{
    componentId: string;      // Component ID
    requestId: string;       // Unique request ID
    type: 'action' | 'sync'; // Request type
    error: {
        message: string;      // Error message
        code: string;        // Error code
        status: number;      // HTTP status code
    };
    duration: number;        // Time before failure (ms)
    timestamp: number;       // Failure timestamp
}

Example:

window.addEventListener('livecomponent:request-failed', (event) => {
    const { componentId, requestId, error, duration } = event.detail;

    activeRequests--;

    // Hide global loading indicator
    if (activeRequests === 0) {
        document.body.classList.remove('loading');
    }

    // Handle network errors
    if (error.status === 0) {
        showToast('Network error. Please check your connection.', 'error');
    } else if (error.status === 429) {
        showToast('Too many requests. Please slow down.', 'warning');
    } else if (error.status >= 500) {
        showToast('Server error. Please try again later.', 'error');
    }

    // Track failures
    analytics.track('request_failed', {
        component: componentId,
        request_id: requestId,
        error_code: error.code,
        status: error.status
    });
});

livecomponent:batch-dispatched

Fired: When batched requests are sent to server.

Use Case: Track batching efficiency, debug batch behavior.

Event Detail:

{
    batchId: string;         // Unique batch ID
    actions: Array<{         // Batched actions
        componentId: string;
        action: string;
        params: object;
    }>;
    batchSize: number;       // Number of actions in batch
    timestamp: number;       // Dispatch timestamp
}

Example:

window.addEventListener('livecomponent:batch-dispatched', (event) => {
    const { batchId, actions, batchSize } = event.detail;

    console.log(`Batch ${batchId} dispatched with ${batchSize} actions:`, actions);

    // Track batching efficiency
    analytics.track('batch_dispatched', {
        batch_id: batchId,
        batch_size: batchSize,
        components: [...new Set(actions.map(a => a.componentId))]
    });
});

Rendering Events

livecomponent:render-start

Fired: Before component re-rendering begins.

Use Case: Save scroll position, prepare for render.

Event Detail:

{
    componentId: string;      // Component ID
    fragments: string[];      // Fragments to be rendered
    timestamp: number;       // Render start timestamp
}

Example:

const scrollPositions = new Map();

window.addEventListener('livecomponent:render-start', (event) => {
    const { componentId, fragments } = event.detail;

    // Save scroll positions of fragments
    fragments.forEach(fragmentName => {
        const fragment = document.querySelector(`[data-lc-fragment="${fragmentName}"]`);
        if (fragment) {
            scrollPositions.set(fragmentName, {
                scrollTop: fragment.scrollTop,
                scrollLeft: fragment.scrollLeft
            });
        }
    });
});

livecomponent:render-complete

Fired: After component re-rendering completes.

Use Case: Restore scroll position, re-initialize JavaScript.

Event Detail:

{
    componentId: string;      // Component ID
    fragments: string[];      // Rendered fragments
    duration: number;        // Render duration (ms)
    timestamp: number;       // Render complete timestamp
}

Example:

window.addEventListener('livecomponent:render-complete', (event) => {
    const { componentId, fragments, duration } = event.detail;

    // Restore scroll positions
    fragments.forEach(fragmentName => {
        const fragment = document.querySelector(`[data-lc-fragment="${fragmentName}"]`);
        const savedPosition = scrollPositions.get(fragmentName);

        if (fragment && savedPosition) {
            fragment.scrollTop = savedPosition.scrollTop;
            fragment.scrollLeft = savedPosition.scrollLeft;
        }
    });

    // Re-initialize JavaScript in rendered fragments
    fragments.forEach(fragmentName => {
        const fragment = document.querySelector(`[data-lc-fragment="${fragmentName}"]`);
        initializeFragment(fragment);
    });

    // Track slow renders
    if (duration > 100) {
        console.warn(`Slow render: ${componentId} took ${duration}ms`);
    }
});

livecomponent:fragment-updated

Fired: When a specific fragment is updated.

Use Case: Re-initialize fragment-specific JavaScript, track updates.

Event Detail:

{
    componentId: string;      // Component ID
    fragmentName: string;    // Updated fragment name
    element: HTMLElement;    // Fragment element
    timestamp: number;       // Update timestamp
}

Example:

window.addEventListener('livecomponent:fragment-updated', (event) => {
    const { componentId, fragmentName, element } = event.detail;

    // Re-initialize specific fragment behaviors
    if (fragmentName === 'chart-container') {
        const chartEl = element.querySelector('.chart');
        if (chartEl) {
            initializeChart(chartEl);
        }
    }

    if (fragmentName === 'image-gallery') {
        const images = element.querySelectorAll('img[data-lazy]');
        lazyLoadImages(images);
    }

    // Track fragment updates
    analytics.track('fragment_updated', {
        component: componentId,
        fragment: fragmentName
    });
});

SSE Events

livecomponent:sse-connected

Fired: When SSE connection is established.

Use Case: Show online status, enable real-time features.

Event Detail:

{
    componentId: string;      // Component ID
    url: string;             // SSE endpoint URL
    timestamp: number;       // Connection timestamp
}

Example:

window.addEventListener('livecomponent:sse-connected', (event) => {
    const { componentId, url } = event.detail;

    console.log(`SSE connected for ${componentId} at ${url}`);

    // Show online indicator
    const indicator = document.querySelector('.connection-status');
    indicator.classList.remove('offline');
    indicator.classList.add('online');
    indicator.textContent = 'Connected';
});

livecomponent:sse-disconnected

Fired: When SSE connection is lost.

Use Case: Show offline status, disable real-time features.

Event Detail:

{
    componentId: string;      // Component ID
    reason: string;          // Disconnection reason
    willReconnect: boolean;  // Auto-reconnect enabled
    timestamp: number;       // Disconnection timestamp
}

Example:

window.addEventListener('livecomponent:sse-disconnected', (event) => {
    const { componentId, reason, willReconnect } = event.detail;

    console.warn(`SSE disconnected for ${componentId}: ${reason}`);

    // Show offline indicator
    const indicator = document.querySelector('.connection-status');
    indicator.classList.remove('online');
    indicator.classList.add('offline');
    indicator.textContent = willReconnect ? 'Reconnecting...' : 'Offline';
});

livecomponent:sse-message

Fired: When an SSE message is received.

Use Case: Handle real-time updates, show notifications.

Event Detail:

{
    componentId: string;      // Component ID
    event: string;           // SSE event type
    data: any;               // Message data
    timestamp: number;       // Message timestamp
}

Example:

window.addEventListener('livecomponent:sse-message', (event) => {
    const { componentId, event: eventType, data } = event.detail;

    if (eventType === 'notification') {
        showNotification(data.title, data.body);
    }

    if (eventType === 'user-joined') {
        updateOnlineUsers(data.userId, data.userName);
    }

    if (eventType === 'price-update') {
        animatePriceChange(data.symbol, data.price);
    }
});

livecomponent:sse-error

Fired: When an SSE error occurs.

Use Case: Handle SSE errors, implement fallback.

Event Detail:

{
    componentId: string;      // Component ID
    error: {
        message: string;      // Error message
        code: string;        // Error code
    };
    timestamp: number;       // Error timestamp
}

Example:

window.addEventListener('livecomponent:sse-error', (event) => {
    const { componentId, error } = event.detail;

    console.error(`SSE error for ${componentId}:`, error.message);

    // Fallback to polling
    if (!window.pollingTimers[componentId]) {
        window.pollingTimers[componentId] = setInterval(() => {
            LiveComponent.getComponent(componentId).executeAction('refresh');
        }, 5000);
    }
});

Upload Events

livecomponent:upload-start

Fired: When file upload starts.

Use Case: Show upload progress UI.

Event Detail:

{
    componentId: string;      // Component ID
    uploadId: string;        // Unique upload ID
    file: {
        name: string;
        size: number;
        type: string;
    };
    chunks: number;          // Total number of chunks
    timestamp: number;       // Upload start timestamp
}

Example:

window.addEventListener('livecomponent:upload-start', (event) => {
    const { uploadId, file, chunks } = event.detail;

    // Create progress UI
    const progressEl = document.createElement('div');
    progressEl.id = `upload-${uploadId}`;
    progressEl.innerHTML = `
        <div class="upload-item">
            <span class="filename">${file.name}</span>
            <div class="progress-bar">
                <div class="progress" style="width: 0%"></div>
            </div>
            <span class="status">Uploading (0/${chunks} chunks)...</span>
        </div>
    `;
    document.querySelector('.upload-queue').appendChild(progressEl);
});

livecomponent:upload-progress

Fired: During file upload (per chunk).

Use Case: Update progress bar, show upload speed.

Event Detail:

{
    componentId: string;      // Component ID
    uploadId: string;        // Upload ID
    progress: {
        loaded: number;       // Bytes uploaded
        total: number;        // Total bytes
        percentage: number;   // Progress percentage
        chunk: number;        // Current chunk
        totalChunks: number;  // Total chunks
    };
    timestamp: number;       // Progress update timestamp
}

Example:

window.addEventListener('livecomponent:upload-progress', (event) => {
    const { uploadId, progress } = event.detail;

    // Update progress bar
    const progressEl = document.querySelector(`#upload-${uploadId}`);
    const progressBar = progressEl.querySelector('.progress');
    const statusEl = progressEl.querySelector('.status');

    progressBar.style.width = `${progress.percentage}%`;
    statusEl.textContent = `Uploading (${progress.chunk}/${progress.totalChunks} chunks) - ${progress.percentage}%`;
});

livecomponent:upload-complete

Fired: When file upload completes successfully.

Use Case: Show success message, clean up progress UI.

Event Detail:

{
    componentId: string;      // Component ID
    uploadId: string;        // Upload ID
    file: {
        name: string;
        size: number;
        url: string;          // Uploaded file URL
    };
    duration: number;        // Upload duration (ms)
    timestamp: number;       // Completion timestamp
}

Example:

window.addEventListener('livecomponent:upload-complete', (event) => {
    const { uploadId, file, duration } = event.detail;

    // Update progress UI
    const progressEl = document.querySelector(`#upload-${uploadId}`);
    const statusEl = progressEl.querySelector('.status');

    statusEl.textContent = `✓ Complete (${(duration / 1000).toFixed(1)}s)`;
    statusEl.classList.add('success');

    // Remove after 3 seconds
    setTimeout(() => progressEl.remove(), 3000);

    // Show success toast
    showToast(`${file.name} uploaded successfully!`, 'success');
});

livecomponent:upload-failed

Fired: When file upload fails.

Use Case: Show error message, allow retry.

Event Detail:

{
    componentId: string;      // Component ID
    uploadId: string;        // Upload ID
    file: {
        name: string;
        size: number;
    };
    error: {
        message: string;      // Error message
        code: string;        // Error code
        chunk: number;       // Failed chunk (if applicable)
    };
    timestamp: number;       // Failure timestamp
}

Example:

window.addEventListener('livecomponent:upload-failed', (event) => {
    const { uploadId, file, error } = event.detail;

    // Update progress UI
    const progressEl = document.querySelector(`#upload-${uploadId}`);
    const statusEl = progressEl.querySelector('.status');

    statusEl.textContent = `✗ Failed: ${error.message}`;
    statusEl.classList.add('error');

    // Add retry button
    const retryBtn = document.createElement('button');
    retryBtn.textContent = 'Retry';
    retryBtn.onclick = () => retryUpload(uploadId);
    progressEl.appendChild(retryBtn);

    // Show error toast
    showToast(`Failed to upload ${file.name}: ${error.message}`, 'error');
});

Error Events

livecomponent:error

Fired: When a general LiveComponent error occurs.

Use Case: Global error handling, error tracking.

Event Detail:

{
    componentId: string;      // Component ID
    error: {
        message: string;      // Error message
        code: string;        // Error code
        stack: string;       // Stack trace
    };
    context: string;         // Error context ('render', 'action', 'network', etc.)
    timestamp: number;       // Error timestamp
}

Example:

window.addEventListener('livecomponent:error', (event) => {
    const { componentId, error, context } = event.detail;

    console.error(`LiveComponent error in ${componentId} (${context}):`, error);

    // Send to error tracking service
    if (window.errorTracker) {
        window.errorTracker.captureException(error, {
            component: componentId,
            context: context
        });
    }

    // Show generic error message to user
    showToast('An unexpected error occurred. Please refresh the page.', 'error');
});

livecomponent:validation-error

Fired: When server-side validation fails.

Use Case: Display validation errors, focus first error.

Event Detail:

{
    componentId: string;      // Component ID
    action: string;          // Action that failed validation
    errors: Record<string, string[]>;  // Validation errors by field
    timestamp: number;       // Error timestamp
}

Example:

window.addEventListener('livecomponent:validation-error', (event) => {
    const { componentId, action, errors } = event.detail;

    // Display errors next to fields
    Object.entries(errors).forEach(([field, messages]) => {
        const input = document.querySelector(`[name="${field}"]`);
        const errorEl = input.parentElement.querySelector('.error-message');

        if (errorEl) {
            errorEl.textContent = messages[0];
            errorEl.style.display = 'block';
        }

        input.classList.add('invalid');
    });

    // Focus first invalid field
    const firstInvalid = document.querySelector('.invalid');
    if (firstInvalid) {
        firstInvalid.focus();
    }
});

livecomponent:rate-limit-exceeded

Fired: When rate limit is exceeded for an action.

Use Case: Show rate limit message, display countdown.

Event Detail:

{
    componentId: string;      // Component ID
    action: string;          // Rate-limited action
    retryAfter: number;      // Seconds until retry allowed
    timestamp: number;       // Rate limit timestamp
}

Example:

window.addEventListener('livecomponent:rate-limit-exceeded', (event) => {
    const { componentId, action, retryAfter } = event.detail;

    // Show countdown toast
    let remaining = retryAfter;
    const toastId = showToast(
        `Rate limit exceeded. Please wait ${remaining} seconds.`,
        'warning',
        { duration: retryAfter * 1000 }
    );

    const countdown = setInterval(() => {
        remaining--;
        if (remaining > 0) {
            updateToast(toastId, `Rate limit exceeded. Please wait ${remaining} seconds.`);
        } else {
            clearInterval(countdown);
            updateToast(toastId, 'You can try again now!');
        }
    }, 1000);
});

Event Patterns

Event Delegation

// Instead of listening on each component
document.addEventListener('livecomponent:action-executed', (event) => {
    if (event.detail.componentId.startsWith('notification-')) {
        updateNotificationBadge();
    }
});

Debounced Event Handlers

import { debounce } from './utils';

window.addEventListener('livecomponent:prop-updated',
    debounce((event) => {
        if (event.detail.propName === 'searchQuery') {
            updateSearchResults(event.detail.currentValue);
        }
    }, 300)
);

Global Loading Indicator

let activeRequests = 0;

window.addEventListener('livecomponent:request-start', () => {
    activeRequests++;
    if (activeRequests === 1) {
        document.body.classList.add('loading');
    }
});

window.addEventListener('livecomponent:request-complete', () => {
    activeRequests--;
    if (activeRequests === 0) {
        document.body.classList.remove('loading');
    }
});

window.addEventListener('livecomponent:request-failed', () => {
    activeRequests--;
    if (activeRequests === 0) {
        document.body.classList.remove('loading');
    }
});

Performance Monitoring

window.addEventListener('livecomponent:action-executed', (event) => {
    const { action, duration } = event.detail;

    if (duration > 1000) {
        console.warn(`Slow action: ${action} (${duration}ms)`);
    }

    // Track to analytics
    analytics.track('action_performance', {
        action: action,
        duration: duration
    });
});

Best Practices

  1. Use Event Delegation: Listen on window instead of individual elements
  2. Cleanup Listeners: Remove listeners in unmounted event
  3. Debounce/Throttle: Use debouncing for high-frequency events
  4. Error Handling: Always handle errors gracefully
  5. Performance: Minimize work in event handlers
  6. Analytics: Track important events for monitoring
  7. User Feedback: Provide visual feedback for all events
  8. Accessibility: Announce important events to screen readers

For more examples and patterns, see: