Skip to main content
Logo
Explore APIsContact Us
  • Home
  • Match Preview
  • Tournament Preview
  • Virtual Stadium
  1. Resources
  2. Widgets
  3. Adding Loading Indicators to Widgets

Adding Loading Indicators to Widgets

#Intended Audience

This tutorial is for:

  • Frontend developers integrating Sportradar widgets
  • UX engineers improving perceived performance during widget loading
  • Developers familiar with basic widget integration
  • Those wanting to enhance user experience with loading states

#Goals

By completing this tutorial, you will:

  • Understand the widget loading lifecycle and timing
  • Implement a custom loading indicator for widgets
  • Use the onTrack callback to detect when widgets are ready
  • Handle the transition from loading state to loaded widget
  • Troubleshoot common loading indicator issues
  • Improve perceived performance with visual feedback

#Prerequisites

Before starting this tutorial, ensure you have:

  • Valid Sportradar Widget license - Contact Sales if needed
  • Basic HTML/CSS/JavaScript knowledge - DOM manipulation, CSS animations, event handling
  • Completed Getting Started Guide - Understanding of basic widget integration
  • Web development environment - Text editor and modern browser
info

This tutorial uses the onTrack callback, which is a core widget feature available for all widget types.


#Overview

#Understanding Widget Loading Phases

Widget loading happens in multiple asynchronous phases:

  1. Script download - Widgetloader framework is fetched
  2. Widget registration - SIR function becomes available
  3. Widget initialization - Widget code replaces target element content
  4. Data fetching - Widget requests match/tournament data
  5. Rendering - Widget displays components or error
warning

The time between widget initialization and data rendering can vary from milliseconds to several seconds depending on network conditions. A loading indicator improves user experience during this wait.

#The Challenge

Users see a blank space while widgets load, which can:

  • Create confusion about whether the page is working
  • Make the page feel slow or broken
  • Reduce user confidence in your integration

A loading indicator provides visual feedback that content is on the way.


#Tutorial Steps

#Step 1: Understand the Basic Widget Integration

Start with a standard widget integration. The widgetloader script downloads asynchronously, then the SIR function loads the widget.

html
<script>
    (function(a,b,c,d,e,f,g,h,i){a[e]||(i=a[e]=function(){(a[e].q=a[e].q||[]).push(arguments)},i.l=1*new Date,i.o=f,
    g=b.createElement(c),h=b.getElementsByTagName(c)[0],g.async=1,g.src=d,g.setAttribute("n",e),h.parentNode.insertBefore(g,h)
    )})(window,document,"script", "https://widgets.sir.sportradar.com/sportradar/widgetloader", "SIR", {
        language: "en"
    });
    SIR("addWidget", "#sr-widget-1", "match.scoreboard", {matchId: 41960917});
</script>

<div class="widgets">
    <div id="sr-widget-1" class="box"></div>
</div>

#What Happens During Loading

Widget Loading Flow
danger

Important: Even though we call SIR("addWidget", ...) immediately, it only executes after the widgetloader downloads. Once executed, it:

  1. Replaces the target element's content with empty widget code
  2. Initializes and starts loading the widget
  3. Fetches required data
  4. Displays components or shows an error

#Step 2: Create the Loading Indicator

Add a separate element for your loading indicator. Keep it outside the widget's target element.

#HTML Structure

html
<div class="widgets">
    <!-- Widget target element - will be populated by widget code -->
    <div id="sr-widget-1" class="box"></div>
    
    <!-- Separate element for loading indicator -->
    <div id="your-content" class="box">
        <div class="loader"></div>
    </div>
</div>
warning

Critical: Never add the loading indicator inside the widget's target element (#sr-widget-1). The widget will replace all content in that element when it initializes.

#CSS Styling

Add styles to size the elements and animate the loader.

View Complete CSS

css
body {
    display: flex;
    justify-content: center;
}

.widgets {
    max-width: 360px;
    width: 100%;
}

/* Set same size for widget and loader containers */
.box {
    border: rgba(0,0,0,0.12) solid 1px;
    min-height: 160px;
}

/* Center loader inside content element */
#your-content {
    display: flex;
    align-items: center;
    justify-content: center;
}

/* Spinning loader animation */
.loader {
    border: 16px solid #f3f3f3;
    border-radius: 50%;
    border-top: 16px solid #3498db;
    width: 40px;
    height: 40px;
    animation: spin 2s linear infinite;
}

@keyframes spin {
    0% { transform: rotate(0deg); }
    100% { transform: rotate(360deg); }
}

#Visual Result

Widget and Loader
tip

You can use any loading indicator style - spinners, skeleton screens, progress bars, or branded animations. Just ensure it provides clear visual feedback.


#Step 3: Implement the Loading State Logic

Use the onTrack callback to detect when the widget has loaded data, then swap visibility between the loader and widget.

#Add the onTrack Callback Function

js
let widgetHasInitialized = false;

function onTrack(target, data) {
    // Listen for 'data_change' event - fired when widget receives data
    if (target === "data_change" && !data.error && !widgetHasInitialized) {
        // Show the widget
        document.getElementById('sr-widget-1').style.display = 'block';
        // Hide the loader
        document.getElementById('your-content').style.display = 'none';
        widgetHasInitialized = true;
    }
}
info

The data_change Event: This event fires when the widget successfully receives data. If data.error is undefined, the widget will render successfully.

#Pass onTrack to Widget Configuration

js
SIR("addWidget", "#sr-widget-1", "match.scoreboard", {
    matchId: 41960917,
    onTrack: onTrack
});

#Hide Widget Initially With CSS

Add CSS to hide the widget element until data loads.

css
/* Hide widget until data loads */
#sr-widget-1 {
    display: none;
}

#Complete Integration

View Complete Working Example

html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Widget with Loading Indicator</title>
    <style>
        body {
            display: flex;
            justify-content: center;
        }
        .widgets {
            max-width: 360px;
            width: 100%;
        }
        .box {
            border: rgba(0,0,0,0.12) solid 1px;
            min-height: 160px;
        }
        #your-content {
            display: flex;
            align-items: center;
            justify-content: center;
        }
        .loader {
            border: 16px solid #f3f3f3;
            border-radius: 50%;
            border-top: 16px solid #3498db;
            width: 40px;
            height: 40px;
            animation: spin 2s linear infinite;
        }
        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }
        /* Hide widget until data loads */
        #sr-widget-1 {
            display: none;
        }
    </style>
</head>
<body>
    <script>
        (function(a,b,c,d,e,f,g,h,i){a[e]||(i=a[e]=function(){(a[e].q=a[e].q||[]).push(arguments)},i.l=1*new Date,i.o=f,
        g=b.createElement(c),h=b.getElementsByTagName(c)[0],g.async=1,g.src=d,g.setAttribute("n",e),h.parentNode.insertBefore(g,h)
        )})(window,document,"script", "https://widgets.sir.sportradar.com/sportradar/widgetloader", "SIR", {
            language: "en"
        });

        let widgetHasInitialized = false;

        function onTrack(target, data) {
            if (target === "data_change" && !data.error && !widgetHasInitialized) {
                document.getElementById('sr-widget-1').style.display = 'block';
                document.getElementById('your-content').style.display = 'none';
                widgetHasInitialized = true;
            }
        }

        SIR("addWidget", "#sr-widget-1", "match.scoreboard", {
            matchId: 41960917,
            onTrack: onTrack
        });
    </script>

    <div class="widgets">
        <div id="sr-widget-1" class="box"></div>
        <div id="your-content" class="box">
            <div class="loader"></div>
        </div>
    </div>
</body>
</html>
tip

Optimization: The widgetHasInitialized flag ensures we only change display once, even if multiple data_change events fire.


#Troubleshooting

#Issue: Loading Indicator Disappears Too Early

Symptom: The loading indicator vanishes before the widget displays, leaving a blank space.

Cause: The loading indicator was placed inside the widget's target element.

Widget Loading Flow
danger

Never add content to the widget's target element. When addWidget executes, it replaces ALL content in the target element with widget code.

Incorrect Implementation:

html
<!-- ❌ DON'T DO THIS -->
<div id="sr-widget-1" class="box">
    <!-- This will be removed when widget initializes! -->
    <div id="your-content" class="box">
        <div class="loader"></div>
    </div>
</div>

Correct Implementation:

html
<!-- ✅ DO THIS -->
<div class="widgets">
    <!-- Widget target - empty, will be filled by widget -->
    <div id="sr-widget-1" class="box"></div>
    
    <!-- Loader in separate element -->
    <div id="your-content" class="box">
        <div class="loader"></div>
    </div>
</div>

#Issue: Widget Shows Before Data Loads

Cause: Forgot to hide the widget element initially with CSS.

Solution: Add CSS to hide the widget until data_change fires:

css
#sr-widget-1 {
    display: none;
}

#Issue: Both Loader and Widget Visible

Cause: The onTrack callback isn't firing or has incorrect logic.

Solution: Verify:

  1. onTrack is passed to widget configuration
  2. Element IDs match exactly
  3. You're checking for target === "data_change"
  4. Browser console shows no JavaScript errors

#Advanced: Handling Errors

You can extend the onTrack callback to handle loading errors:

js
function onTrack(target, data) {
    if (target === "data_change" && !widgetHasInitialized) {
        widgetHasInitialized = true;
        
        if (data.error) {
            // Handle error state
            document.getElementById('your-content').innerHTML = 
                '<p style="color: red;">Failed to load widget</p>';
        } else {
            // Show widget on success
            document.getElementById('sr-widget-1').style.display = 'block';
            document.getElementById('your-content').style.display = 'none';
        }
    }
}

#Key Takeaways

Separate Elements

Always keep the loading indicator in a separate element from the widget's target. The widget replaces its target element's content.

Use onTrack Callback

The onTrack callback with data_change event is the reliable way to detect when a widget has loaded and is ready to display.

Hide Widget Initially

Start with the widget hidden (display: none) and only show it after the data_change event confirms successful loading.


#Further Reading

#Core Documentation

  • Getting Started Guide - Basic widget integration
  • Widget Events (onTrack) - Complete event reference
  • Widget Configuration - All configuration options

#Related Tutorials

  • Widget Visibility in Carousels - Managing visibility in dynamic layouts
  • Error Handling - Comprehensive error handling guide

#Performance Resources

  • Web.dev: Perceived Performance - Why loading indicators matter
  • MDN: CSS Animations - Creating custom loaders
  • MDN: display Property - Understanding display values
Last updated 14 days ago
Is this site helpful?
Widgets, Engagement Tools
Widget Visibility in Carousels and SlidersConditional Widget Display With Error Handling
On this page
  • Intended Audience
  • Goals
  • Prerequisites
  • Overview
  • Understanding Widget Loading Phases
  • The Challenge
  • Tutorial Steps
  • Step 1: Understand the Basic Widget Integration
  • Step 2: Create the Loading Indicator
  • Step 3: Implement the Loading State Logic
  • Troubleshooting
  • Issue: Loading Indicator Disappears Too Early
  • Issue: Widget Shows Before Data Loads
  • Issue: Both Loader and Widget Visible
  • Advanced: Handling Errors
  • Key Takeaways
  • Further Reading
  • Core Documentation
  • Related Tutorials
  • Performance Resources