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
| Embed | Placement | |
|---|---|---|
| Identifiers | embed_id + api_key | placement_id + style_id + api_key |
| Content | A fixed, pre-configured feed | A content slot whose feed is resolved by the platform |
| Layout | Baked into the embed | Controlled separately by the style_id (carousel / feed / grid / section) |
| Best when | You want one specific, unchanging feed | You 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):
| Value | Attribute | What it is |
|---|---|---|
| Placement ID | data-placement-id / placement_id | Identifies the content slot. |
| Style ID | data-style-id / style_id | Identifies the layout/visual style for that placement. |
| API Key | data-api-key / api_key | Authenticates 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
- Add a container <
div> where the experience should appear. Give it a unique id and the gen-sdk-class class. - Load the SDK script from the Genuin CDN.
- 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.
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.
<!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.
<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.
<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.
"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):
<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.
// 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>
);
}// 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.
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:
| Style | Typical container | Notes |
|---|---|---|
| Carousel | width: 100%; height: 600px | Horizontal swipeable row, full-width. |
| Feed (vertical) | width: 300–400px; height: 600px | Single vertical column, TikTok-style. |
| Grid | width: 400px; height: 800px | Multi-row thumbnail grid. |
| Section (placement-section) | width: 100%; height: 600px | In-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()).
<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>:
<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).
<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:
<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.
| Field | Type | Notes |
|---|---|---|
| placement_id | string | Placement (content slot) id. Required with style_id for the placement path. |
| style_id | string | Layout/style id. Required with placement_id. |
| api_key | string | Brand API key. Required. Also the shared fallback for multi-div setups. |
| token | string | Autologin token to authenticate the current user. |
| theme | "light" | "dark" | Color theme. |
| start_video_slug | string | Deep-link to a specific video. |
| contextual_params | object | page_context, geo {lat, long}, url, etc. for contextual feeds. |
| authInfo | object | Auth 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.
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):
| Method | What 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.
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:
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.
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
| Symptom | Likely 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 ID | Two containers share an id. Make each unique. |
| Embed shows instead of placement | The 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 undefined | init() 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.