Skip to main content
Logo
Explore APIsContact Us
  • Home
  • Match Preview
  • Tournament Preview
  • Virtual Stadium
  • StatsHub
  1. Engagement Tools
  2. Widget Integration Best Practices

Widget Integration Best Practices

#What Are Widget Integration Best Practices?

Professional widget integration goes beyond basic implementation to ensure reliable, performant, and maintainable deployments. This tutorial covers advanced techniques for production-ready widget integration including:

  • Performance optimization - Minimize load times and resource usage
  • Error handling strategies - Graceful degradation and user feedback
  • Production deployment - Monitoring, logging, and maintenance
  • User experience enhancement - Loading states, responsiveness, and accessibility
  • Security considerations - Content Security Policy and safe integration

The practices outlined here build upon fundamental integration knowledge to deliver enterprise-grade widget implementations.

#Intended Audience

This tutorial is designed for:

  • Frontend developers with experience integrating Sportradar widgets
  • Technical leads responsible for production deployments
  • DevOps engineers implementing monitoring and deployment strategies
  • Product managers seeking to understand technical requirements for optimal widget performance

Prerequisites: Basic familiarity with widget integration, JavaScript, and web development concepts.

#Goals

By completing this tutorial, you will:

  • Implement robust error handling and fallback strategies
  • Optimize widget performance for production environments
  • Set up comprehensive monitoring and logging systems
  • Apply security best practices for safe widget integration
  • Create responsive, accessible widget implementations
  • Establish maintenance workflows for long-term reliability

#Environment Specification (Prerequisites)

To complete this tutorial, you will need:

#Required Tools and Access

  • Sportradar Widgets Account with API credentials
  • Development Environment with modern browser (Chrome 90+, Firefox 88+, Safari 14+)
  • Text Editor or IDE (VS Code, WebStorm, or similar)
  • Local Web Server for testing (Live Server, http-server, or similar)
  • Browser Developer Tools for debugging and performance analysis

#Technical Requirements

  • JavaScript Knowledge - ES6+, Promises, async/await
  • HTML/CSS Skills - DOM manipulation, responsive design
  • Basic Node.js (optional) - for build tools and local development
  • Network Access - to Sportradar APIs and CDN resources

#Optional but Recommended

  • Monitoring Tools - Google Analytics, LogRocket, or similar APM solution
  • Build Tools - Webpack, Rollup, or Vite for optimization
  • Testing Framework - Jest, Cypress, or Playwright for automated testing

#Part 1: Advanced Error Handling and Resilience

Professional widget integration requires comprehensive error handling that gracefully manages various failure scenarios.

#Step 1: Implement Widget Load Error Detection

First, let's create a robust widget loader with comprehensive error detection:

html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Production Widget Integration</title>
    <style>
        .widget-container {
            position: relative;
            min-height: 200px;
            background: #f5f5f5;
            border-radius: 8px;
            overflow: hidden;
        }
        
        .widget-loading {
            display: flex;
            align-items: center;
            justify-content: center;
            height: 200px;
            color: #666;
        }
        
        .widget-error {
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            height: 200px;
            color: #d32f2f;
            text-align: center;
            padding: 20px;
        }
        
        .retry-button {
            margin-top: 10px;
            padding: 8px 16px;
            background: #1976d2;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
    </style>
</head>
<body>
    <div id="scoreboard-widget" class="widget-container">
        <div class="widget-loading">Loading match data...</div>
    </div>

    <script>
        class WidgetManager {
            constructor(containerId, widgetConfig) {
                this.container = document.getElementById(containerId);
                this.config = widgetConfig;
                this.retryCount = 0;
                this.maxRetries = 3;
                this.retryDelay = 2000;
                this.loadTimeout = 15000;
            }

            async loadWidget() {
                try {
                    await this.loadWidgetScript();
                    await this.initializeWidget();
                    this.onSuccess();
                } catch (error) {
                    this.handleError(error);
                }
            }
        }

        // Initialize widget manager
        const widgetManager = new WidgetManager('scoreboard-widget', {
            widget: 'sr:match:scoreboard',
            matchId: 12345678
        });
    </script>
</body>
</html>

#Step 2: Add Comprehensive Script Loading Logic

javascript
class WidgetManager {
    constructor(containerId, widgetConfig) {
        this.container = document.getElementById(containerId);
        this.config = widgetConfig;
        this.retryCount = 0;
        this.maxRetries = 3;
        this.retryDelay = 2000;
        this.loadTimeout = 15000;
        this.scriptLoadPromise = null;
    }

    async loadWidgetScript() {
        if (this.scriptLoadPromise) {
            return this.scriptLoadPromise;
        }

        this.scriptLoadPromise = new Promise((resolve, reject) => {
            // Check if widget script is already loaded
            if (window.SRWidgets) {
                resolve();
                return;
            }

            const script = document.createElement('script');
            script.src = 'https://widgets.sir.sportradar.com/js/api/widgets.js';
            script.async = true;
            script.crossOrigin = 'anonymous';
            
            // Set up timeout
            const timeout = setTimeout(() => {
                reject(new Error('Widget script load timeout'));
            }, this.loadTimeout);

            script.onload = () => {
                clearTimeout(timeout);
                // Wait for widget API to be ready
                this.waitForWidgetAPI().then(resolve).catch(reject);
            };

            script.onerror = () => {
                clearTimeout(timeout);
                reject(new Error('Widget script failed to load'));
            };

            document.head.appendChild(script);
        });

        return this.scriptLoadPromise;
    }

    async waitForWidgetAPI() {
        return new Promise((resolve, reject) => {
            let attempts = 0;
            const maxAttempts = 50;
            
            const checkAPI = () => {
                if (window.SRWidgets && typeof window.SRWidgets.render === 'function') {
                    resolve();
                } else if (attempts >= maxAttempts) {
                    reject(new Error('Widget API not available after loading'));
                } else {
                    attempts++;
                    setTimeout(checkAPI, 100);
                }
            };
            
            checkAPI();
        });
    }
}

#Step 3: Implement Widget Initialization With Fallbacks

javascript
class WidgetManager {
    // ... previous methods

    async initializeWidget() {
        return new Promise((resolve, reject) => {
            const initTimeout = setTimeout(() => {
                reject(new Error('Widget initialization timeout'));
            }, this.loadTimeout);

            try {
                const widgetConfig = {
                    ...this.config,
                    onload: () => {
                        clearTimeout(initTimeout);
                        console.log('Widget loaded successfully');
                        resolve();
                    },
                    onerror: (error) => {
                        clearTimeout(initTimeout);
                        reject(new Error(`Widget initialization failed: ${error.message || error}`));
                    },
                    ontrack: (event) => {
                        this.trackWidgetEvent(event);
                    }
                };

                // Clear loading state
                this.container.innerHTML = '';
                
                // Initialize widget
                window.SRWidgets.render(this.container, widgetConfig);
                
            } catch (error) {
                clearTimeout(initTimeout);
                reject(error);
            }
        });
    }

    trackWidgetEvent(event) {
        // Integration with analytics
        if (window.gtag) {
            gtag('event', 'widget_interaction', {
                'event_category': 'widget',
                'event_label': event.type,
                'custom_parameter': event.data
            });
        }
        
        // Custom logging
        console.log('Widget event:', event);
    }

    handleError(error) {
        console.error('Widget error:', error);
        
        // Show user-friendly error message
        this.showErrorState(error);
        
        // Attempt retry if within limits
        if (this.retryCount < this.maxRetries) {
            this.scheduleRetry();
        } else {
            this.showFallbackContent();
        }
        
        // Report error to monitoring service
        this.reportError(error);
    }
}

#Part 2: Performance Optimization Strategies

#Step 4: Implement Lazy Loading and Resource Optimization

javascript
class PerformantWidgetManager extends WidgetManager {
    constructor(containerId, widgetConfig) {
        super(containerId, widgetConfig);
        this.intersectionObserver = null;
        this.isVisible = false;
        this.resourcesPreloaded = false;
    }

    initLazyLoading() {
        if (!('IntersectionObserver' in window)) {
            // Fallback for older browsers
            this.loadWidget();
            return;
        }

        this.intersectionObserver = new IntersectionObserver(
            (entries) => {
                entries.forEach(entry => {
                    if (entry.isIntersecting && !this.isVisible) {
                        this.isVisible = true;
                        this.preloadResources();
                        setTimeout(() => this.loadWidget(), 100);
                        this.intersectionObserver.disconnect();
                    }
                });
            },
            {
                rootMargin: '100px',
                threshold: 0.1
            }
        );

        this.intersectionObserver.observe(this.container);
    }

    async preloadResources() {
        if (this.resourcesPreloaded) return;
        
        const resources = [
            'https://widgets.sir.sportradar.com/css/api/widgets.css',
            'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap'
        ];

        const preloadPromises = resources.map(url => {
            return new Promise((resolve) => {
                const link = document.createElement('link');
                link.rel = 'preload';
                link.as = url.includes('.css') ? 'style' : 'script';
                link.href = url;
                link.onload = resolve;
                link.onerror = resolve; // Don't fail on preload errors
                document.head.appendChild(link);
            });
        });

        await Promise.allSettled(preloadPromises);
        this.resourcesPreloaded = true;
    }

    async loadWidget() {
        // Show skeleton loading instead of basic loading
        this.showSkeletonLoader();
        
        try {
            await super.loadWidget();
        } catch (error) {
            this.handleError(error);
        }
    }

    showSkeletonLoader() {
        this.container.innerHTML = `
            <div class="skeleton-loader">
                <div class="skeleton-header"></div>
                <div class="skeleton-content">
                    <div class="skeleton-team">
                        <div class="skeleton-logo"></div>
                        <div class="skeleton-name"></div>
                    </div>
                    <div class="skeleton-score"></div>
                    <div class="skeleton-team">
                        <div class="skeleton-logo"></div>
                        <div class="skeleton-name"></div>
                    </div>
                </div>
            </div>
        `;
    }
}

// Usage with performance monitoring
const performantManager = new PerformantWidgetManager('scoreboard-widget', {
    widget: 'sr:match:scoreboard',
    matchId: 12345678
});

// Start performance monitoring
performance.mark('widget-init-start');
performantManager.initLazyLoading();

#Part 3: Production Monitoring and Maintenance

#Step 5: Implement Comprehensive Monitoring

javascript
class MonitoredWidgetManager extends PerformantWidgetManager {
    constructor(containerId, widgetConfig) {
        super(containerId, widgetConfig);
        this.metrics = {
            loadStartTime: null,
            scriptLoadTime: null,
            widgetRenderTime: null,
            totalLoadTime: null,
            errorCount: 0,
            retryCount: 0
        };
        this.setupGlobalErrorHandling();
    }

    setupGlobalErrorHandling() {
        // Capture unhandled widget errors
        window.addEventListener('error', (event) => {
            if (event.filename && event.filename.includes('widgets.sir.sportradar.com')) {
                this.reportError({
                    type: 'script_error',
                    message: event.message,
                    filename: event.filename,
                    lineno: event.lineno,
                    colno: event.colno
                });
            }
        });

        // Capture promise rejections
        window.addEventListener('unhandledrejection', (event) => {
            if (event.reason && event.reason.toString().includes('widget')) {
                this.reportError({
                    type: 'promise_rejection',
                    message: event.reason.toString(),
                    stack: event.reason.stack
                });
            }
        });
    }

    async loadWidget() {
        this.metrics.loadStartTime = performance.now();
        performance.mark('widget-load-start');
        
        try {
            await super.loadWidget();
            this.recordSuccessMetrics();
        } catch (error) {
            this.metrics.errorCount++;
            this.handleError(error);
        }
    }

    recordSuccessMetrics() {
        const endTime = performance.now();
        this.metrics.totalLoadTime = endTime - this.metrics.loadStartTime;
        
        performance.mark('widget-load-end');
        performance.measure('widget-total-load', 'widget-load-start', 'widget-load-end');
        
        // Report metrics to analytics
        this.reportMetrics({
            widget_load_time: this.metrics.totalLoadTime,
            widget_type: this.config.widget,
            match_id: this.config.matchId,
            retry_count: this.retryCount,
            success: true
        });
        
        // Log to console for debugging
        console.log('Widget Metrics:', this.metrics);
    }

    reportMetrics(metrics) {
        // Google Analytics 4
        if (window.gtag) {
            gtag('event', 'widget_performance', {
                custom_map: { metric_1: 'load_time' },
                metric_1: Math.round(metrics.widget_load_time)
            });
        }
        
        // Custom analytics endpoint
        if (this.config.analyticsEndpoint) {
            fetch(this.config.analyticsEndpoint, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({
                    timestamp: new Date().toISOString(),
                    page_url: window.location.href,
                    user_agent: navigator.userAgent,
                    ...metrics
                })
            }).catch(err => console.warn('Analytics reporting failed:', err));
        }
    }

    reportError(error) {
        const errorReport = {
            timestamp: new Date().toISOString(),
            page_url: window.location.href,
            user_agent: navigator.userAgent,
            widget_type: this.config.widget,
            match_id: this.config.matchId,
            error_type: error.type || 'unknown',
            error_message: error.message,
            stack_trace: error.stack,
            retry_count: this.retryCount,
            metrics: this.metrics
        };

        // Log to console
        console.error('Widget Error Report:', errorReport);

        // Send to error tracking service
        if (window.Sentry) {
            Sentry.captureException(error, {
                tags: {
                    component: 'widget',
                    widget_type: this.config.widget
                },
                extra: errorReport
            });
        }

        // Custom error endpoint
        if (this.config.errorEndpoint) {
            fetch(this.config.errorEndpoint, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(errorReport)
            }).catch(err => console.warn('Error reporting failed:', err));
        }
    }
}

#Part 4: Security and Accessibility Best Practices

#Step 6: Implement Security Hardening

html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Secure Widget Integration</title>
    
    <!-- Content Security Policy -->
    <meta http-equiv="Content-Security-Policy" content="
        default-src 'self';
        script-src 'self' 'unsafe-inline' https://widgets.sir.sportradar.com;
        style-src 'self' 'unsafe-inline' https://widgets.sir.sportradar.com https://fonts.googleapis.com;
        font-src 'self' https://fonts.gstatic.com;
        img-src 'self' data: https://widgets.sir.sportradar.com https://*.sportradar.com;
        connect-src 'self' https://api.sportradar.com https://widgets.sir.sportradar.com;
        frame-src 'none';
        object-src 'none';
    ">
    
    <!-- Additional security headers -->
    <meta http-equiv="X-Content-Type-Options" content="nosniff">
    <meta http-equiv="X-Frame-Options" content="DENY">
    <meta http-equiv="Referrer-Policy" content="strict-origin-when-cross-origin">
</head>
<body>
    <main>
        <h1>Live Match Scoreboard</h1>
        <div 
            id="scoreboard-widget" 
            class="widget-container"
            role="region"
            aria-label="Live match scoreboard"
            aria-live="polite"
            aria-busy="true"
        >
            <div class="widget-loading" aria-label="Loading match data">
                <span class="sr-only">Loading match data, please wait...</span>
                <div class="spinner" aria-hidden="true"></div>
                Loading match data...
            </div>
        </div>
    </main>

    <script>
        class SecureWidgetManager extends MonitoredWidgetManager {
            constructor(containerId, widgetConfig) {
                super(containerId, widgetConfig);
                this.validateConfiguration();
                this.setupSecurityHeaders();
            }

            validateConfiguration() {
                // Validate required parameters
                if (!this.config.matchId || typeof this.config.matchId !== 'number') {
                    throw new Error('Invalid or missing matchId');
                }
                
                if (!this.config.widget || typeof this.config.widget !== 'string') {
                    throw new Error('Invalid or missing widget type');
                }
                
                // Sanitize string inputs
                if (this.config.title) {
                    this.config.title = this.sanitizeString(this.config.title);
                }
                
                // Validate numeric inputs
                if (this.config.seasonId && !Number.isInteger(this.config.seasonId)) {
                    throw new Error('Invalid seasonId - must be an integer');
                }
            }

            sanitizeString(input) {
                if (typeof input !== 'string') return '';
                
                // Remove potentially dangerous characters
                return input
                    .replace(/[<>]/g, '') // Remove angle brackets
                    .replace(/javascript:/gi, '') // Remove javascript: protocol
                    .replace(/on\w+=/gi, '') // Remove event handlers
                    .trim()
                    .substring(0, 200); // Limit length
            }

            setupSecurityHeaders() {
                // Verify CSP is in place
                const metaCSP = document.querySelector('meta[http-equiv="Content-Security-Policy"]');
                if (!metaCSP) {
                    console.warn('Content Security Policy not found - consider adding CSP headers');
                }
                
                // Check for secure context
                if (!window.isSecureContext) {
                    console.warn('Insecure context detected - widgets should be served over HTTPS');
                }
            }

            async loadWidgetScript() {
                // Verify script integrity
                const expectedOrigin = 'https://widgets.sir.sportradar.com';
                
                if (!this.config.allowInsecure && !window.location.protocol.startsWith('https')) {
                    throw new Error('Widgets require HTTPS in production');
                }

                return super.loadWidgetScript();
            }

            initializeWidget() {
                // Add accessibility attributes
                this.container.setAttribute('aria-busy', 'false');
                this.container.setAttribute('aria-live', 'polite');
                
                return super.initializeWidget();
            }

            onSuccess() {
                // Update accessibility state
                this.container.setAttribute('aria-busy', 'false');
                this.container.removeAttribute('aria-label');
                
                // Announce success to screen readers
                this.announceToScreenReader('Match data loaded successfully');
                
                super.onSuccess();
            }

            announceToScreenReader(message) {
                const announcement = document.createElement('div');
                announcement.setAttribute('aria-live', 'assertive');
                announcement.setAttribute('aria-atomic', 'true');
                announcement.style.position = 'absolute';
                announcement.style.left = '-10000px';
                announcement.style.width = '1px';
                announcement.style.height = '1px';
                announcement.style.overflow = 'hidden';
                
                document.body.appendChild(announcement);
                announcement.textContent = message;
                
                setTimeout(() => {
                    document.body.removeChild(announcement);
                }, 1000);
            }
        }

        // Initialize secure widget manager
        const secureManager = new SecureWidgetManager('scoreboard-widget', {
            widget: 'sr:match:scoreboard',
            matchId: 12345678,
            allowInsecure: false // Enforce HTTPS in production
        });

        secureManager.initLazyLoading();
    </script>
    
    <style>
        .sr-only {
            position: absolute !important;
            width: 1px !important;
            height: 1px !important;
            padding: 0 !important;
            margin: -1px !important;
            overflow: hidden !important;
            clip: rect(0, 0, 0, 0) !important;
            border: 0 !important;
        }
        
        .spinner {
            width: 20px;
            height: 20px;
            border: 2px solid #f3f3f3;
            border-top: 2px solid #1976d2;
            border-radius: 50%;
            animation: spin 1s linear infinite;
            display: inline-block;
            margin-right: 10px;
        }
        
        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }
        
        .widget-container {
            border: 1px solid #e0e0e0;
            border-radius: 8px;
            padding: 16px;
            background: white;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }
        
        .widget-container:focus-within {
            outline: 2px solid #1976d2;
            outline-offset: 2px;
        }
    </style>
</body>
</html>

#Part 5: Production Deployment and Maintenance

#Step 7: Create Deployment Checklist and Monitoring

javascript
class ProductionWidgetManager extends SecureWidgetManager {
    constructor(containerId, widgetConfig) {
        super(containerId, widgetConfig);
        this.deploymentChecks = [];
        this.healthCheckInterval = null;
        this.performanceObserver = null;
        this.runDeploymentChecks();
    }

    async runDeploymentChecks() {
        console.log('🚀 Running production deployment checks...');
        
        const checks = [
            { name: 'HTTPS', check: () => window.location.protocol === 'https:' },
            { name: 'CSP', check: () => document.querySelector('meta[http-equiv="Content-Security-Policy"]') },
            { name: 'Error Handling', check: () => typeof this.reportError === 'function' },
            { name: 'Analytics', check: () => window.gtag || this.config.analyticsEndpoint },
            { name: 'Accessibility', check: () => this.container.hasAttribute('aria-label') },
            { name: 'Performance Monitoring', check: () => 'PerformanceObserver' in window },
            { name: 'Valid Configuration', check: () => this.validateProductionConfig() }
        ];

        for (const check of checks) {
            try {
                const passed = await check.check();
                this.deploymentChecks.push({
                    name: check.name,
                    status: passed ? 'PASS' : 'FAIL',
                    timestamp: new Date().toISOString()
                });
                console.log(`${passed ? '✅' : '❌'} ${check.name}`);
            } catch (error) {
                this.deploymentChecks.push({
                    name: check.name,
                    status: 'ERROR',
                    error: error.message,
                    timestamp: new Date().toISOString()
                });
                console.log(`❌ ${check.name}: ${error.message}`);
            }
        }

        this.reportDeploymentStatus();
    }

    validateProductionConfig() {
        const requiredFields = ['widget', 'matchId'];
        const missingFields = requiredFields.filter(field => !this.config[field]);
        
        if (missingFields.length > 0) {
            throw new Error(`Missing required fields: ${missingFields.join(', ')}`);
        }

        // Check for development-only settings
        if (this.config.debug && window.location.hostname !== 'localhost') {
            console.warn('Debug mode enabled in production environment');
        }

        return true;
    }

    async loadWidget() {
        // Start health monitoring
        this.startHealthMonitoring();
        
        // Initialize performance monitoring
        this.initPerformanceMonitoring();
        
        try {
            await super.loadWidget();
            this.reportDeploymentSuccess();
        } catch (error) {
            this.reportDeploymentFailure(error);
            throw error;
        }
    }

    startHealthMonitoring() {
        // Periodic health checks
        this.healthCheckInterval = setInterval(() => {
            this.performHealthCheck();
        }, 60000); // Check every minute

        // Clean up on page unload
        window.addEventListener('beforeunload', () => {
            if (this.healthCheckInterval) {
                clearInterval(this.healthCheckInterval);
            }
            if (this.performanceObserver) {
                this.performanceObserver.disconnect();
            }
        });
    }

    performHealthCheck() {
        const healthMetrics = {
            timestamp: new Date().toISOString(),
            widget_responsive: this.isWidgetResponsive(),
            memory_usage: this.getMemoryUsage(),
            error_rate: this.calculateErrorRate(),
            load_time: this.metrics.totalLoadTime
        };

        // Report to monitoring service
        if (this.config.healthCheckEndpoint) {
            fetch(this.config.healthCheckEndpoint, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(healthMetrics)
            }).catch(err => console.warn('Health check reporting failed:', err));
        }

        // Log locally for debugging
        console.log('Health Check:', healthMetrics);
    }

    isWidgetResponsive() {
        try {
            // Check if widget container has content
            const hasContent = this.container.children.length > 0;
            
            // Check if widget is visible
            const rect = this.container.getBoundingClientRect();
            const isVisible = rect.width > 0 && rect.height > 0;
            
            return hasContent && isVisible;
        } catch (error) {
            return false;
        }
    }

    getMemoryUsage() {
        if ('memory' in performance) {
            return {
                used: Math.round(performance.memory.usedJSHeapSize / 1048576), // MB
                total: Math.round(performance.memory.totalJSHeapSize / 1048576), // MB
                limit: Math.round(performance.memory.jsHeapSizeLimit / 1048576) // MB
            };
        }
        return null;
    }

    calculateErrorRate() {
        const totalAttempts = this.retryCount + 1;
        return totalAttempts > 0 ? (this.metrics.errorCount / totalAttempts) * 100 : 0;
    }

    initPerformanceMonitoring() {
        if ('PerformanceObserver' in window) {
            this.performanceObserver = new PerformanceObserver((list) => {
                for (const entry of list.getEntries()) {
                    if (entry.name.includes('widget')) {
                        this.reportPerformanceEntry(entry);
                    }
                }
            });

            this.performanceObserver.observe({ entryTypes: ['measure', 'navigation', 'resource'] });
        }
    }

    reportPerformanceEntry(entry) {
        const performanceData = {
            name: entry.name,
            duration: entry.duration,
            startTime: entry.startTime,
            entryType: entry.entryType,
            timestamp: new Date().toISOString()
        };

        console.log('Performance Entry:', performanceData);
        
        // Report to analytics
        this.reportMetrics(performanceData);
    }

    reportDeploymentStatus() {
        const report = {
            timestamp: new Date().toISOString(),
            page_url: window.location.href,
            user_agent: navigator.userAgent,
            checks: this.deploymentChecks,
            overall_status: this.deploymentChecks.every(check => check.status === 'PASS') ? 'HEALTHY' : 'ISSUES_DETECTED'
        };

        console.log('📊 Deployment Status Report:', report);
        
        if (this.config.deploymentReportEndpoint) {
            fetch(this.config.deploymentReportEndpoint, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(report)
            }).catch(err => console.warn('Deployment reporting failed:', err));
        }
    }

    reportDeploymentSuccess() {
        performance.mark('widget-deployment-success');
        console.log('🎉 Widget deployment successful');
        
        this.reportMetrics({
            deployment_status: 'success',
            deployment_time: performance.now(),
            environment: this.getEnvironment()
        });
    }

    reportDeploymentFailure(error) {
        performance.mark('widget-deployment-failure');
        console.error('💥 Widget deployment failed:', error);
        
        this.reportError({
            type: 'deployment_failure',
            message: error.message,
            stack: error.stack,
            environment: this.getEnvironment(),
            deployment_checks: this.deploymentChecks
        });
    }

    getEnvironment() {
        const hostname = window.location.hostname;
        if (hostname === 'localhost' || hostname === '127.0.0.1') return 'development';
        if (hostname.includes('staging') || hostname.includes('test')) return 'staging';
        return 'production';
    }
}

// Production deployment with comprehensive configuration
const productionManager = new ProductionWidgetManager('scoreboard-widget', {
    widget: 'sr:match:scoreboard',
    matchId: 12345678,
    
    // Analytics endpoints
    analyticsEndpoint: 'https://analytics.yoursite.com/api/events',
    errorEndpoint: 'https://monitoring.yoursite.com/api/errors',
    healthCheckEndpoint: 'https://monitoring.yoursite.com/api/health',
    deploymentReportEndpoint: 'https://monitoring.yoursite.com/api/deployment',
    
    // Security settings  
    allowInsecure: false,
    
    // Performance settings
    maxRetries: 3,
    loadTimeout: 15000,
    
    // Feature flags
    debug: false,
    enableHealthChecks: true,
    enablePerformanceMonitoring: true
});

// Initialize with full monitoring
productionManager.initLazyLoading();

#Further Reading

#Official Documentation

  • Widget Integration Getting Started Guide
  • Scoreboard Widget Integration Example
  • Widget Theming and Customization Tutorial

#Performance Resources

  • Web Performance Best Practices - Google's comprehensive performance guide
  • Intersection Observer API - MDN documentation for lazy loading
  • Performance API - Timing and metrics collection

#Security Guidelines

  • Content Security Policy - MDN CSP implementation guide
  • OWASP Frontend Security - Industry security standards
  • Web Security Essentials - Google's security best practices

#Accessibility Standards

  • WCAG 2.1 Guidelines - Web accessibility standards
  • ARIA Authoring Practices - Screen reader compatibility guide
  • Accessibility Testing Tools - Testing and validation resources

#Monitoring and Analytics

  • Google Analytics 4 - Event tracking implementation
  • Sentry Error Tracking - Error monitoring setup
  • Web Vitals - User experience metrics

info

This tutorial provides production-ready patterns for professional widget integration. Always test thoroughly in staging environments before deploying to production, and monitor performance metrics continuously to ensure optimal user experience.

Last updated about 1 month ago
Is this site helpful?
Widgets, Engagement Tools, BET
AccessibilityTroubleshooting
On this page
  • What Are Widget Integration Best Practices?
  • Intended Audience
  • Goals
  • Environment Specification (Prerequisites)
  • Required Tools and Access
  • Technical Requirements
  • Optional but Recommended
  • Part 1: Advanced Error Handling and Resilience
  • Step 1: Implement Widget Load Error Detection
  • Step 2: Add Comprehensive Script Loading Logic
  • Step 3: Implement Widget Initialization With Fallbacks
  • Part 2: Performance Optimization Strategies
  • Step 4: Implement Lazy Loading and Resource Optimization
  • Part 3: Production Monitoring and Maintenance
  • Step 5: Implement Comprehensive Monitoring
  • Part 4: Security and Accessibility Best Practices
  • Step 6: Implement Security Hardening
  • Part 5: Production Deployment and Maintenance
  • Step 7: Create Deployment Checklist and Monitoring
  • Further Reading
  • Official Documentation
  • Performance Resources
  • Security Guidelines
  • Accessibility Standards
  • Monitoring and Analytics