How do you configure the Genuin Web SDK for Placement on your website?

If you are looking for the simpler single-feed integration, see the Embed integration guide.

This guide is for developers integrating Genuin's Commerce Media Network into a website using a Placement. A placement lets you render Genuin video content on your own site without redirecting users away, keeping engagement, watch time, and conversions on your page.

Placement vs. Embed - Pick the Right Path

EmbedPlacement
Identifiersembed_id + api_keyplacement_id + style_id + api_key
ContentA fixed, pre-configured feedA content slot whose feed is resolved by the platform
LayoutBaked into the embedControlled separately by the style_id (carousel / feed / grid / section)
Best whenYou want one specific, unchanging feedYou want to manage content and layout independently from the dashboard

A placement decouples what plays (placement_id) from how it looks (style_id), so you can change either side from the Genuin dashboard without touching your site code.

What the SDK Does

  • Renders a Genuin video experience (carousel, vertical feed, grid, or in-page section) directly inside a container <div> on your page.
  • Isolates all of its styles inside a Shadow DOM so it never leaks CSS into - or inherits CSS from - your site.
  • Lazy-loads the player when the container scrolls near the viewport.
  • Auto-resizes the container height to fit the rendered content.
  • Optionally authenticates your logged-in users (autologin) and surfaces playback events.

Prerequisites

Get these three values from your Genuin dashboard (or your Genuin contact):

ValueAttributeWhat it is
Placement IDdata-placement-id / placement_idIdentifies the content slot.
Style IDdata-style-id / style_idIdentifies the layout/visual style for that placement.
API Keydata-api-key / api_keyAuthenticates your brand. Required for every integration.

All three are required for the placement path. If either placement_id or style_id is missing (and no embed_id is provided), the SDK throws "Placement ID or Style ID is missing, and no embed ID provided" and renders nothing.

Steps to Embed a Placement

  1. Add a container <div> where the experience should appear. Give it a unique id and the gen-sdk-class class.
  2. Load the SDK script from the Genuin CDN.
  3. Initialize with your placement_id, style_id, and api_key - either via data-attributes on the div, or via genuin.init({...}).

How It Works - the Integration Flow

This is what happens end-to-end, from your HTML to a rendered player. You only write the first two boxes; everything below the dotted line is the SDK's job.

index.html
YOUR PAGE
  +-----------------------------------------------------------+
  | 1. Container div                                          |
  |    <div id="gen-sdk" class="gen-sdk-class"                 |
  |         data-placement-id="..." data-style-id="..."        |
  |         data-api-key="...">                                |
  |                                                             |
  | 2. <script src=".../gen_sdk.min.js">  -- loads the loader  |
  |                                                             |
  | 3. genuin.init({ api_key })           -- you call this     |
  +-----------------------------+-------------------------------+
                                |
  ..............................|...............................
  SDK (automatic)                v
  +-----------------------------------------------------------+
  | a. Loader queues your init() call until the SDK module     |
  |    loads                                                   |
  | b. Discovers every container (id "gen-sdk", id              |
  |    "gen-sdk-*", or class "gen-sdk-class") on the page       |
  | c. Reads each container's data-attributes -> builds its     |
  |    config                                                   |
  |      placement_id + style_id present -> PLACEMENT path      |
  |      (placement wins over any embed_id on the same div)     |
  | d. Fetches brand details (api_key) + placement data          |
  |      (placement_id resolves the feed, style_id resolves      |
  |      layout)                                                |
  | e. Attaches a Shadow DOM so SDK styles stay isolated         |
  | f. Shows a skeleton, lazy-loads the player when it scrolls   |
  |    near                                                      |
  | g. Renders carousel / feed / grid / section per the style_id |
  | h. Auto-resizes the container height to fit the content      |
  +-----------------------------------------------------------+

Key Points About the Flow

  • You can call genuin.init() immediately after the script tag - even before the SDK has finished downloading. The loader queues the call and runs it once ready, so you don't have to wait for a “ready” signal. (window.onGenuinReady still exists if you prefer a callback - see SDK APIs.)
  • The div is the source of truth. Each container carries its own placement_id / style_id via data-attributes, so one genuin.init({ api_key }) call wires up every placement on the page.
  • Placement = content + layout, decoupled. placement_id decides what plays; style_id decides how it looks. Change either from the dashboard without editing your site.

Implementation

1. HTML / Vanilla JavaScript

There are two equivalent ways to pass the placement config: data-attributes on the div, or the init() object. Use whichever fits your stack.

Option A - Config via init() (Single Placement)
html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Genuin Placement Demo</title>
  </head>
  <body>
    <!-- 1. Container -->
    <div
      id="gen-sdk"
      class="gen-sdk-class"
      style="width: 100%; height: 600px;"
    ></div>
 
    <!-- 2. SDK script from CDN.  <env> = "qa" for QA, omitted for production. -->
    <script src="https://media.begenuin.com/sdk/2.0.6/gen_sdk.min.js"></script>
 
    <!-- 3. Initialize -->
    <script>
      window.genuin.init({
        placement_id: "6a22bb8afa804e909cf577fe",
        style_id: "6a22bb8afa804e909cf577ff",
        api_key: "3d9fbaa9ee0777b4c9bbf15303f7ce8108394b5dd9759af4",
      });
    </script>
  </body>
</html>

The SDK auto-discovers any <div> with id="gen-sdk", an id starting with gen-sdk-, or the gen-sdk-class class. Put the source on the div, and call init() with just the api_key.

Option B - Config via Data-Attributes
typescript
<div
  id="gen-sdk"
  class="gen-sdk-class"
  data-placement-id="6a22bb8afa804e909cf577fe"
  data-style-id="6a22bb8afa804e909cf577ff"
  data-api-key="3d9fbaa9ee0777b4c9bbf15303f7ce8108394b5dd9759af4"
  style="width: 100%; height: 600px;"
></div>
<script src="https://media.begenuin.com/sdk/2.0.6/gen_sdk.min.js"></script>
<script>
  window.genuin.init({
    api_key: "3d9fbaa9ee0777b4c9bbf15303f7ce8108394b5dd9759af4",
  });
</script>

Precedence: if a div carries both data-placement-id + data-style-id and data-embed-id, the placement path wins and the embed id is ignored.

Give each container a unique id, put its own source on each div, and call init() once. Duplicate ids log a warning and break initialization.

Multiple Placements on One Page
typescript
<div
  id="gen-sdk-1"
  class="gen-sdk-class"
  data-placement-id="69e74417c389e59d070a4521"
  data-style-id="69e74417c389e59d070a4522"
  data-api-key="3d9fbaa9ee0777b4c9bbf15303f7ce8108394b5dd9759af4"
  style="width: 300px; height: 600px;"
></div>
<div
  id="gen-sdk-2"
  class="gen-sdk-class"
  data-placement-id="69e74498c389e59d070a4556"
  data-style-id="69e74498c389e59d070a4557"
  data-api-key="3d9fbaa9ee0777b4c9bbf15303f7ce8108394b5dd9759af4"
  style="width: 400px; height: 800px;"
></div>
<script src="https://media.begenuin.com/sdk/2.0.6/gen_sdk.min.js"></script>
<script>
  // api_key in init() is the shared fallback; per-div data-api-key overrides it.
  window.genuin.init({
    api_key: "3d9fbaa9ee0777b4c9bbf15303f7ce8108394b5dd9759af4",
  });
</script>

2. React

Load the SDK script once, render the container with data-attributes, and call init() in an effect. You don't need to wait for the SDK to finish loading - the loader queues the call. The only thing to guard against is double-unit on re-render, which the use Ref flag below handles.

index.html
typescript
"use client";
 
import { useEffect, useRef } from "react";
 
export function GenuinPlacement() {
  const initialized = useRef(false);
 
  useEffect(() => {
    if (initialized.current || !window.genuin) return;
    initialized.current = true;
 
    // Safe to call immediately -- the loader queues this until the SDK is ready.
    window.genuin.init({
      api_key: "3d9fbaa9ee0777b4c9bbf15303f7ce8108394b5dd9759af4",
    });
  }, []);
 
  return (
    <div
      id="gen-sdk"
      className="gen-sdk-class"
      data-placement-id="6a22bb8afa804e909cf577fe"
      data-style-id="6a22bb8afa804e909cf577ff"
      data-api-key="3d9fbaa9ee0777b4c9bbf15303f7ce8108394b5dd9759af4"
      style={{ width: "100%", height: 600 }}
    />
  );
}

Add the script tag once in your app shell (e.g. index.html or a layout):

index.html
typescript
<script src="https://media.begenuin.com/sdk/2.0.6/gen_sdk.min.js" async></script>

3. Next.js (App Router)

Use the Script component with the before. Interactive strategy, and a client component for the container + init.

index.html
typescript
// app/layout.tsx
import Script from "next/script";
 
export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        {children}
        <Script
          src="https://media.begenuin.com/sdk/2.0.6/gen_sdk.min.js"
          strategy="beforeInteractive"
        />
      </body>
    </html>
  );
}
index.html
typescript
// components/genuin-placement.tsx
"use client";
 
import { useEffect, useRef } from "react";
 
export function GenuinPlacement() {
  const initialized = useRef(false);
 
  useEffect(() => {
    if (initialized.current || !window.genuin) return;
    initialized.current = true;
 
    window.genuin.init({
      api_key: "3d9fbaa9ee0777b4c9bbf15303f7ce8108394b5dd9759af4",
    });
  }, []);
 
  return (
    <div
      id="gen-sdk"
      className="gen-sdk-class"
      data-placement-id="6a22bb8afa804e909cf577fe"
      data-style-id="6a22bb8afa804e909cf577ff"
      data-api-key="3d9fbaa9ee0777b4c9bbf15303f7ce8108394b5dd9759af4"
      style={{ width: "100%", height: 600 }}
    />
  );
}

4. Angular

Inject the script in a component and init on AfterViewInit.

index.html
typescript
import { Component, AfterViewInit } from "@angular/core";
 
@Component({
  selector: "app-genuin-placement",
  template: `
    <div
      id="gen-sdk"
      class="gen-sdk-class"
      data-placement-id="6a22bb8afa804e909cf577fe"
      data-style-id="6a22bb8afa804e909cf577ff"
      data-api-key="3d9fbaa9ee0777b4c9bbf15303f7ce8108394b5dd9759af4"
      style="width: 100%; height: 600px;"
    ></div>
  `,
})
export class GenuinPlacementComponent implements AfterViewInit {
  ngAfterViewInit(): void {
    const script = document.createElement("script");
    script.src = "https://media.begenuin.com/sdk/2.0.6/gen_sdk.min.js";
    script.onload = () => {
      (window as any).genuin.init({
        api_key: "3d9fbaa9ee0777b4c9bbf15303f7ce8108394b5dd9759af4",
      });
    };
    document.body.appendChild(script);
  }
}

Layout Styles

The style_id decides how the placement renders. These are the styles you can configure from the dashboard, with representative sizing:

StyleTypical containerNotes
Carouselwidth: 100%; height: 600pxHorizontal swipeable row, full-width.
Feed (vertical)width: 300–400px; height: 600pxSingle vertical column, TikTok-style.
Gridwidth: 400px; height: 800pxMulti-row thumbnail grid.
Section (placement-section)width: 100%; height: 600pxIn-page sectioned layout.

You don't choose the style in code - you set it on the placement's style_id in the dashboard, and the SDK renders accordingly. The container style (width/height) you pass is just the host box; the SDK auto-adjusts height to the rendered content.

Customization

Container Size

Set width / height on the container div (inline style, a CSS class, or the style object in the multi-div pattern). The SDK respects your width and auto-resizes height to fit.

Theme

Pass data-theme="light" or data-theme="dark" on the div (or theme in init()).

index.html
typescript
<div
  id="gen-sdk"
  class="gen-sdk-class"
  data-placement-id="6a22bb8afa804e909cf577fe"
  data-style-id="6a22bb8afa804e909cf577ff"
  data-api-key="3d9fbaa9ee0777b4c9bbf15303f7ce8108394b5dd9759af4"
  data-theme="light"
  style="width: 100%; height: 600px;"
></div>

Font Family

Override the SDK font by targeting .gen-sdk-class in your page <head>:

index.html
typescript
<style>
  .gen-sdk-class { font-family: "Inter", sans-serif !important; }
</style>

Deep-Link to a Specific Video

Pass data-start-video-slug (or start_video_slug in init()) to open the player directly on a given video (opens the expand view on load).

index.html
typescript
<div
  id="gen-sdk"
  class="gen-sdk-class"
  data-placement-id="6a22bb8afa804e909cf577fe"
  data-style-id="6a22bb8afa804e909cf577ff"
  data-api-key="3d9fbaa9ee0777b4c9bbf15303f7ce8108394b5dd9759af4"
  data-start-video-slug="my-video-slug"
  style="width: 100%; height: 600px;"
></div>

Contextual Feed (Personalization)

For placements configured to use context, pass page context and/or geo so the feed adapts:

index.html
typescript
<div
  id="gen-sdk"
  class="gen-sdk-class"
  data-placement-id="6a3c30e8d2c57cd3546c2106"
  data-style-id="6a3c30e8d2c57cd3546c2107"
  data-api-key="188e43c588c69904abefc500378b803e31d90f40495e53dd"
  data-page-context="AI, LLM, Machine Learning"
  data-lat="37.7749"
  data-long="-122.4194"
  style="width: 100%; height: 600px;"
></div>

SDK APIs

genuin.init(config)

Initializes every Genuin container found on the page. Call once.

FieldTypeNotes
placement_idstringPlacement (content slot) id. Required with style_id for the placement path.
style_idstringLayout/style id. Required with placement_id.
api_keystringBrand API key. Required. Also the shared fallback for multi-div setups.
tokenstringAutologin token to authenticate the current user.
theme"light" | "dark"Color theme.
start_video_slugstringDeep-link to a specific video.
contextual_paramsobjectpage_context, geo {lat, long}, url, etc. for contextual feeds.
authInfoobjectAuth options, e.g. signInUrl for custom login redirect.

Per-container data-attributes override the matching init() value, so multiple placements can each carry their own source while sharing a single init() call.

genuin.update(config)

Update a placement after init without reinitializing the whole SDK. Pass container_id (the div id) plus the fields to change - e.g. new contextual_params, a token to authenticate, or a start_video_slug.

index.html
typescript
window.genuin.update({
  container_id: "gen-sdk-1",
  contextual_params: { page_context: "Sports, Football" },
});

Programmatic Player Controls

These methods are exposed on the global and, like init, are safe to call early (they queue until the SDK is ready):

MethodWhat it does
genuin.expand(options)Open the full-screen expand view programmatically.
genuin.collapse(options)Close the expand view.
genuin.logout()Clear the authenticated user.
genuin.off(event, cb)Remove an event listener added with genuin.on.

Ready Callback (Optional)

If you prefer a callback over calling init() directly, define window.onGenuinReady; the SDK invokes it once loaded. In most cases you don't need this - just call genuin.init(), which queues automatically.

index.html
typescript
window.onGenuinReady = (sdk) => {
  // sdk is ready here
};

Authentication Callback

To run your own login flow when a user tries an action that requires auth, define window.genuinAuth before init(). The SDK calls it instead of using its built-in flow:

index.html
typescript
window.genuinAuth = (data) => {
  // data carries returnQueryParams for context.
  // Open your login, then call genuin.update({ container_id, token }) once you have a token.
  window.open("https://your-site.com/login", "_blank");
};

You can also pass authInfo.signInUrl in init() to point the SDK at your sign-in page.

Playback & Engagement Events

Listen for events the player emits. Supported event names include:

onPlay, onPause, onMuteChange, onVideoNotFound, onSwipedForward, onSwipedBackward, onExpandViewChanged, onAnalyticsTrack, onFeedLoaded, onFollowChanged, onShare, onVideoClicked.

index.html
typescript
window.genuin.on?.("onPlay", (payload) => {
  console.log("video playing", payload);
});

Best Practices

  • Unique ids. Every container must have a unique id. Duplicate ids log a warning and break init.
  • Call init() once, guard against double-init. You don't need to wait for the SDK to load - the loader queues your call. In SPA frameworks, just guard against calling init() twice across re-renders (a useRef/module flag), since the loader will run each queued call.
  • Pin the SDK version. Use a specific <version> in the CDN URL (e.g. 2.0.6) rather than a floating one, so a platform update can't change behavior unexpectedly.
  • Keep the API key brand-scoped. The browser-side API key is meant for client embedding; never reuse server-side secrets here.
  • Let the SDK size height. Set width; the SDK auto-resizes height to the content.
  • Don't pass both paths. Provide either placement_id + style_id or embed_id - not both (placement wins if you do).

Troubleshooting

SymptomLikely cause
Nothing renders, console error "Placement ID or Style ID is missing…"You passed only one of placement_id / style_id. Both are required.
[Genuin SDK Warning]: Found N elements with the same IDTwo containers share an id. Make each unique.
Embed shows instead of placementThe div has both an embed id and a placement id; placement should win — verify the data-attributes are spelled correctly (data-placement-id, data-style-id).
genuin is undefinedinit() ran before the CDN script loaded. Guard on window.onGenuinReady.

Next Steps

  • Configure the placement and its style in the Genuin dashboard.
  • Wire up autologin via token / genuinAuth so engagement is attributed to your users.
  • Subscribe to playback events for your own analytics.
  • For a single fixed feed instead of a managed slot, use the Embed integration guide.

© 2026 Genuin Inc.

Genuin Footer