Skip to content

Advanced Embed Patterns

This page covers advanced embed integration patterns for developers who need more control over the FanFest embed behavior.

Custom Login Callback

The hostLoginFn callback lets you replace the embed's built-in login with your own authentication flow. When a user clicks "Login" inside the embed, the SDK calls your function instead of showing the FanFest login form.

javascript
FanFestSDK.init({
  clientId: "your-web-app-id",
  hostLoginFn: () => {
    // Example: redirect to your Auth0 login
    auth0Client.loginWithRedirect({
      redirect_uri: window.location.origin + "/auth/callback",
    });
  },
});

Requires SSO integration

Custom login callbacks only work when you have SSO configured. Without SSO, the embed uses FanFest's built-in authentication and the hostLoginFn is ignored.

How it works:

  1. The user clicks a login trigger inside the embed
  2. The embed sends a PostMessage to the SDK
  3. The SDK calls your hostLoginFn
  4. Your function handles the authentication (redirect, modal, etc.)
  5. After successful auth, the SDK's silent authentication syncs the user state back to the embed

Custom Rewards Callback

The hostRewardsFn callback lets you direct users to your own rewards experience instead of the embed's built-in rewards view.

javascript
FanFestSDK.init({
  clientId: "your-web-app-id",
  hostRewardsFn: () => {
    // Example: open a rewards modal on your site
    document.getElementById("rewards-modal").showModal();
  },
});

Requires SSO integration

Custom rewards callbacks only work when you have SSO configured. Without SSO, the embed handles rewards display internally and the hostRewardsFn is ignored.

Embed Lifecycle

The embed communicates with the host page through a PostMessage bridge. Here is the sequence of events during initialization:

Host Page                    SDK                         Embed Iframe
    |                         |                              |
    |-- init(options) ------->|                              |
    |                         |-- Create iframe ------------>|
    |                         |-- MutationObserver setup     |
    |                         |                              |
    |                         |<-- EmbedReady ---------------|
    |                         |-- Authentication ----------->|
    |                         |                              |
    |                         |<-- AuthenticationFinished ---|
    |                         |                              |

Key lifecycle events:

  1. SDK Initializationinit() validates options, fetches web app config, and creates the embed iframe
  2. Embed Ready — The iframe content loads and signals readiness via embed-ready PostMessage
  3. Authentication — The SDK sends the current auth state to the embed
  4. Authentication Finished — The embed confirms authentication is complete and reports the user state

The PostMessage bridge validates message origins against the configured appOrigin for security. Messages from unknown origins are silently ignored.

Multiple Embeds on One Page

The SDK supports multiple embed containers on the same page. Each <div id="fanfest-embed"> element gets its own iframe.

html
<!-- Main content area -->
<div id="fanfest-embed" style="width: 100%; height: 600px;"></div>

<!-- Sidebar widget -->
<div id="fanfest-embed" style="width: 300px; height: 400px;"></div>

Same channel

All embed instances on a page share the same clientId and display the same channel. The SDK does not currently support showing different channels in different embed containers on the same page.

Behavior notes:

  • All embed instances share the same authentication state — logging in through one embed authenticates all of them
  • The SDK tracks each container independently and creates a separate iframe for each
  • Removing a container from the DOM causes the SDK to automatically clean up that embed instance
  • Dynamic containers are supported — add a <div id="fanfest-embed"> at any time and the SDK's MutationObserver picks it up

Responsive Embed Sizing

The embed iframe fills 100% of its container's width and height. To make the embed responsive, style the container:

Fixed Height

html
<div id="fanfest-embed" style="width: 100%; height: 600px;"></div>

Viewport-Relative Height

html
<div id="fanfest-embed" style="width: 100%; height: 80vh;"></div>

Full-Page Embed

css
#fanfest-embed {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  z-index: 1000;
}

Responsive with Aspect Ratio

css
#fanfest-embed {
  width: 100%;
  aspect-ratio: 16 / 9;
}

Mobile-Responsive Layout

css
#fanfest-embed {
  width: 100%;
  height: 600px;
}

@media (max-width: 768px) {
  #fanfest-embed {
    height: 100vh;
    position: fixed;
    top: 0;
    left: 0;
  }
}

Action Mapping with registerActionMapping

The registerActionMapping method maps custom action names (used in your tracking code) to channel action IDs (configured in the FanFest dashboard). This is required for event tracking to correctly associate tracked events with leaderboard actions and point rewards.

Why Action Mapping Matters

When you track an event like FanFestSDK.trackEvent({ action: "purchase" }), the SDK needs to know which channel action ID this corresponds to. Action mappings create that link between your application's event names and FanFest's internal action system.

Three Input Formats

The SDK accepts three formats for registering action mappings. All three are validated at runtime and produce the same result internally.

Format 1: Single Object

Register one mapping at a time:

javascript
FanFestSDK.registerActionMapping({
  action: "purchase",
  channelActionId: "ca_abc123",
});

Format 2: Array of Objects

Register multiple mappings in a single call:

javascript
FanFestSDK.registerActionMapping([
  { action: "purchase", channelActionId: "ca_abc123" },
  { action: "page_view", channelActionId: "ca_def456" },
  { action: "share", channelActionId: "ca_ghi789" },
]);

Register multiple mappings using a key-value record where keys are action names and values are channel action IDs:

javascript
FanFestSDK.registerActionMapping({
  purchase: "ca_abc123",
  page_view: "ca_def456",
  share: "ca_ghi789",
  signup: "ca_jkl012",
  referral: "ca_mno345",
});

Recommended format

The bulk record format is the most concise and readable, especially when registering many mappings. We recommend this format for production use.

When to Register Mappings

Register action mappings after init() completes but before tracking any events:

javascript
await FanFestSDK.init({ clientId: "your-web-app-id" });

// Register all action mappings up front
FanFestSDK.registerActionMapping({
  purchase: "ca_abc123",
  page_view: "ca_def456",
  share: "ca_ghi789",
});

// Now tracking works correctly
FanFestSDK.trackEvent({ action: "purchase" });

Finding Channel Action IDs

Channel action IDs are created in the FanFest dashboard under your channel's leaderboard settings. Each action you create in the dashboard gets a unique ID that you use in your action mappings. See the Leaderboards guide for details on creating channel actions.

Validation Errors

If you pass an invalid payload to registerActionMapping, the SDK logs an error to the console but does not throw. This prevents a misconfigured mapping from breaking your application.

javascript
// This logs an error — values must be strings
FanFestSDK.registerActionMapping({
  purchase: 12345, // Error: expected string
});

Initialization Guard

The SDK prevents double initialization. If you call init() more than once, the second call is ignored and a warning is logged:

javascript
await FanFestSDK.init({ clientId: "your-web-app-id" });
await FanFestSDK.init({ clientId: "other-id" }); // Ignored, logs warning

This is safe to call in application code that might run multiple times (e.g., in a React useEffect or Vue onMounted hook).

Security Model

The embed uses several security mechanisms:

  • Origin validation — PostMessage events are only accepted from the configured appOrigin
  • Iframe sandbox — The embed iframe runs with a restricted sandbox: allow-same-origin, allow-scripts, allow-forms, allow-modals, allow-popups
  • Function bindinghostLoginFn and hostRewardsFn are bound to the window object to prevent exposing your function scope to the embed
  • Schema validation — All incoming PostMessage data is validated against a strict schema before processing

Next Steps

Released under the MIT License.