@pandascore/iframe-sdk
v0.4.0
Published
Pandascore SDK for iframe communication
Readme
PandaScore Iframe SDK
PandaScore Iframe SDK is built to help parent applications communicate reliably with the PandaScore sportsbook iframe. It lets you initialize the iframe, exchange typed messages, react to iframe navigation requests, and keep user context (such as balance) in sync.
Features
- Secure parent/iframe communication powered by
MessageChannel. - Typed TypeScript API
- Automatic iframe bootstrap and config handshake.
- Two navigation modes: parent-driven or iframe-driven.
- ESM, CJS, and global bundle support for flexible integration.
Table of Contents
Installation
Install the SDK with yarn:
yarn add @pandascore/iframe-sdkIf you use a script tag integration, build the SDK (or download it from the package) and load the global bundle:
<script src="path/to/pandascore-sdk.iife.js"></script>The global bundle exposes PandascoreSDK on window.
Usage
To use the SDK, create a new PandascoreSDK instance and initialize it with your iframe settings and runtime options:
const sdk = new PandascoreSDK({
container: document.getElementById('iframe-container')!,
customerId: 'your-customer-id',
token: 'your-auth-token',
iframeUrl: 'https://iframe.pandascore.co',
debugMode: false,
rootUrl: '/play',
onNavigate: (path, applyNavigation) => {
// Optional: parent app handles routing, then notifies iframe
applyNavigation();
},
onIframeStatusUpdate: state => {
console.log('SDK state:', state);
},
options: {
balance: {
amount: 1000,
currency: 'USD'
},
initialPath: '/',
lang: 'en-GB',
oddsFormat: 'decimal',
theme: 'default'
}
});Constructor parameters
Required parameters
container(Element): DOM element where the iframe will be mounted.customerId(string): Your PandaScore customer ID.token(string): Authentication token used by the iframe.options(object): Runtime iframe options.options.balance(object): User balance payload.options.balance.amount(number): Non-negative integer amount.options.balance.currency(string): Currency code.
Optional parameters
rootUrl(string): Parent app path prefix after the origin. Must start with/.origin(string): Origin of the page that embeds the iframe — the page running this SDK. Defaults to that page's own origin, so you normally omit it. Seeoriginvsparents.parents(string[]): Origins of the frames your integration is itself nested inside, if any — required for live streams to play in a nested embed. Not an allowlist, and never includesorigin. Seeoriginvsparents.onNavigate((path: string, applyNavigation: () => void) => void): Optional callback fired when iframe requests navigation.onIframeStatusUpdate((state: PandascoreIframeStatus) => void): Callback fired when SDK lifecycle state changes.debugMode(boolean): Enables SDK debug logs when set totrue.options.initialPath(string): Initial path inside iframe.options.lang(string): Language code.options.oddsFormat('decimal' | 'american' | 'fractional'): How odds are displayed. Defaults to'decimal'.'fractional'shows conventional bookmaker fractions (10/11,6/4, …).options.theme(string): Theme identifier.
Specificities
rootUrl behavior
rootUrl defines where your iframe integration lives inside your parent app, after the domain origin.
- Parent app origin:
https://website.com rootUrl: '/iframe'- Iframe internal route:
/live - Generated absolute link:
https://website.com/iframe/esports
If your integration is mounted at the site root, you can omit this parameter.
Valid examples:
'/iframe''/sportsbook''/'
Invalid examples:
'iframe'(missing leading slash)'https://website.com/iframe'(must be path only, not full URL)
origin vs parents
These two describe different things and are easy to confuse. origin is the page your integration runs on. parents is what that page is itself embedded in.
origin — a single origin: the page that directly embeds the PandaScore iframe. The SDK defaults it to its own page's origin, so most integrations never set it.
It is passed to the iframe explicitly rather than inferred from document.referrer, because browsers strip or trim the referrer under Referrer-Policy: no-referrer and same-origin. The iframe uses it as the target origin of the config handshake, and — together with rootUrl — to build absolute links back into your app.
parents — the origins of any frames sitting above that page, for integrations that are themselves nested. origin is never a member of this list, and neither is a security allowlist: it describes the actual frame chain, not a set of permitted embedders. Leave it empty (the default) when the page running the SDK is the top-level document.
It matters because the iframe embeds live match streams, and stream providers only play inside a frame whose full ancestor chain is declared up front. The iframe knows the page that embeds it, but not what that page is embedded in — parents is how it finds out. Get it wrong on a nested integration and the stream stays blank while everything else on the page works.
A white-label sportsbook embedded in an operator portal:
https://portal.com ← top-level document
└── https://sportsbook.com/betting ← runs the PandaScore SDK
└── https://iframe.pandascore.co ← the PandaScore iframenew PandascoreSDK({
// ...
origin: 'https://sportsbook.com', // the immediate embedder (the default anyway)
parents: ['https://portal.com'], // the frames above it, closest first
rootUrl: '/betting' // where the integration lives inside sportsbook.com
});Mounted directly on sportsbook.com with nothing above it, the same integration needs neither: origin defaults correctly and parents stays [].
SDK status updates (onIframeStatusUpdate)
Use onIframeStatusUpdate to observe the SDK lifecycle and react in your app (loading states, fallbacks, or teardown logic).
onIframeStatusUpdate can receive:
'INITIALIZING': SDK constructor passed validation and initialization started.'INITIALIZED': Message channel is ready and communication with the iframe is active.'FAILED': Initialization failed due to invalid constructor params or setup issues.'DESTROYED':destroy()was called and resources/listeners were cleaned up.
Navigation modes
The SDK automatically picks one of two navigation behaviors, depending on if you're using the onNavigate callback.
1) Parent-driven navigation (onNavigate provided)
When the iframe requests a navigation, the SDK triggers onNavigate(path, applyNavigation) in your parent app. Your app decides whether and how to navigate (router guards, redirects, analytics, permissions, etc.).
The path value always starts with /.
After your app applies navigation, notify the iframe by calling either:
applyNavigation()from inside the onNavigate callback (helper bound to the requestedpath)sdk.navigate(path)if you're handling navigation elsewhere (explicit call)
This requires more integration work, but it gives three important benefits:
- Shareable pages (URL stays synchronized in the parent app).
- Links can be opened in new tabs.
- Back/forward browser arrows work with proper parent/iframe sync.
const sdk = new PandascoreSDK({
// ...other params
onNavigate: (path, applyNavigation) => {
console.log('Iframe requested path:', path);
// Example: router.push(path)
applyNavigation();
},
options: {
balance: { amount: 1000, currency: 'USD' }
}
});When you call
sdk.navigate(path), the SDK automatically strips the configuredrootUrl(if present) before sending the path to the iframe. In most integrations, you do not need to handle that prefix yourself. It is still recommended to handle it defensively in your own router integration to avoid corner cases.
2) Iframe-driven navigation (onNavigate omitted)
If onNavigate is not provided, the iframe handles navigation internally. To avoid broken behavior, iframe links are rendered without shareable hrefs:
- They cannot be opened in a new tab.
- Their link URL cannot be copied from standard link interactions.
Methods
Navigate
To programmatically trigger a navigation in the iframe, call sdk.navigate(path).
See the previous section Navigation modes for detailed information about navigation behaviors and integration with your app's router.
Send balance updates
Call sendBalance whenever the user balance changes in the parent app.
sdk.sendBalance({
amount: 1500,
currency: 'USD'
});Destroy the SDK instance
Call destroy when the iframe should be unmounted or when leaving the page.
sdk.destroy();