Skip to content

JavaScript API

Everything goes through one function, SportNinja.push([command, ...args]). Start every script with the queue line, so commands work whether or not embed.js has loaded yet:

window.SportNinja = window.SportNinja || [];

Before the loader arrives, push just adds to the queue; the loader runs the queued commands in order when it starts. Commands never throw: a problem is logged to the console with a [SportNinja] prefix.

SportNinja.push(['mount', target, options]);
  • target: a CSS selector ('#sportninja') or an element. The plugin’s iframe is added inside it.
  • options: see the Options Reference.

Each element holds one plugin; mounting on an element that already has one is ignored with a warning.

Change the look or layout of a mounted plugin without reloading it:

SportNinja.push(['update', { theme: { mode: 'dark' }, display: { hideHeader: true } }]);
  • Only theme, tabs and display can change. Anything else (key, view, the IDs, initialTab, syncUrl) is fixed at mount; to change those, unmount and mount again.
  • Each section you send replaces that whole section. Send the full theme you want, not just the key that changed.
  • Values must be plain data (no functions or DOM objects).

Example: follow your site’s dark-mode toggle.

const theme = { primary: '#1C7ED6' };
function setDark(isDark) {
SportNinja.push(['update', { theme: { ...theme, mode: isDark ? 'dark' : 'light' } }]);
}
SportNinja.push(['unmount']); // the only plugin on the page
SportNinja.push(['unmount', '#sportninja']); // a specific one

Removes the plugin’s iframe. The element itself stays.

SportNinja.push(['on', 'registration:completed', (data, { element }) => {
console.log('Registered', data.registrationId, 'in', element);
}]);

The handler gets the event data and { element }, the element the plugin is mounted on. Event data carries IDs only, never personal details.

Event When Data
ready The plugin has loaded { view, id }: the entry page and its ID
signin A visitor signs in inside the plugin { userId }
signout A visitor signs out {}
registration:completed A visitor finishes a registration { registrationId, sessionId }

A ready handler added after the plugin is already ready is still called once, straight away. Handlers apply to every plugin on the page; use element to tell them apart.

Example: send registrations to your analytics.

SportNinja.push(['on', 'registration:completed', (data) => {
window.gtag?.('event', 'sportninja_registration', { registration_id: data.registrationId });
}]);

Give each plugin its own element and mount call. With more than one plugin, update and unmount need the target first:

SportNinja.push(
['mount', '#standings', { key, view: 'competition', competitionId, initialTab: 'standings', syncUrl: false }],
['mount', '#schedule', { key, view: 'competition', competitionId, initialTab: 'schedule' }],
);
SportNinja.push(['update', '#standings', { display: { hideHeader: true } }]);

Only the first plugin can sync the address. Set syncUrl: false on the others to make that explicit.

  • Load embed.js once, e.g. in your page template.
  • Mount when the container element is in the DOM (after render), and unmount before it’s removed, so a later mount on a new element works.
// React
useEffect(() => {
const element = ref.current;
window.SportNinja = window.SportNinja || [];
window.SportNinja.push(['mount', element, options]);
return () => window.SportNinja.push(['unmount', element]);
}, []);
  • Leave syncUrl on only if your router doesn’t use the hash; the plugin keeps its page in #sn=….