Add a Genuin placement to your website

Render a placement with Web SDK 2.0.6 so each page gets the videos its context calls for, in the layout you manage from Brand Control Center.

A placement is a content slot. Where an embed shows one feed you picked, a placement resolves its videos when the page loads, from the placement's own rules and the context your page sends. Its layout comes from a separate style, so you can change what plays and how it looks from Brand Control Center without touching your site.

Placements use the same script and the same init call as embeds. This page covers what's different; for loading the script in each framework, the full option list, events, and browser requirements, see Embed the Genuin Web SDK on your website.

How a placement differs from an embed

EmbedPlacement
Identifiersembed_id and api_keyplacement_id, style_id, and api_key
ContentOne feed you chose: brand, communities, or groupsA feed type plus communities, groups, and network brands, filtered by the style's rules and ranked per section
LayoutPart of the embedSet by the style: carousel, feed, grid, or dynamic, with optional titled sections
Page contextRanks the feed when the embed has no fixed community or groupDeclared per placement under Implement; sent as contextual_params
Change laterEdit the embedEdit the placement or swap the style; the ids stay the same

When a container carries both a placement and an embed id, the placement wins and the embed id is ignored.

Before you begin

RequirementDetail
A placement and a styleCreated under Grow > On Site > Placement. See Create a placement and map it to the shopper journey.
Placement ID and style IDBoth 24-character ids, shown on the placement's Implement tab and in its generated code.
API keyYour brand's browser key, the same one embeds use.
Script and containerThe SDK script pinned to 2.0.6 and a sized container, as in Embed the Genuin Web SDK on your website.

How the integration flows

You write the first three items. The SDK does the rest on every page load.

  1. A container <div> with the class gen-sdk-class and a unique id.
  2. The script tag for https://media.begenuin.com/sdk/2.0.6/gen_sdk.min.js.
  3. One window.genuin.init call with your api_key, and the ids either in the call or as data-* attributes.
  4. The loader queues the call, imports the SDK bundle, and scans the page for containers.
  5. For each container it reads the data attributes; placement_id plus style_id selects the placement path.
  6. It fetches your brand by api_key, then the placement by placement_id, and picks the style by style_id.
  7. It attaches a Shadow DOM, shows a skeleton, and renders the player when the container is within 200 px of the viewport.
  8. It requests the feed with your contextual_params, renders the style's layout, and sets the container height to fit.

Step 1: Copy the placement ID and style ID

  1. In Brand Control Center, go to Grow > On Site, click Placement, and open your placement.
  2. Open the style you want to render and click Configuration.
  3. On the right, click Implement. The tab lists the Style ID and each context signal the placement expects, with its parameter name.
The Implement tab names every parameter the placement expects and shows the style ID.
  1. Click View Embed Code. The Web tab carries placement_id, style_id, api_key, and a contextual_params block with a placeholder for each signal you ticked. Copy the ids from there.

Step 2: Add the container and the script

index.html
html
<div id="gen-sdk" class="gen-sdk-class" style="width: 100%; height: 600px;"></div>
<script src="https://media.begenuin.com/sdk/2.0.6/gen_sdk.min.js"></script>

Set the width you want. The style's Placement Size in Brand Control Center decides the player's own dimensions, and the SDK sets the container height to the rendered content.

Step 3: Initialize the placement

Pass both ids with the key. Setting only one of placement_id and style_id falls back to the embed path and fails.

index.html
html
<script>
  window.genuin.init({
    placement_id: "{{PLACEMENT_ID}}",
    style_id: "{{STYLE_ID}}",
    api_key: "<<YOUR_API_KEY>>",
  });
</script>

For several placements on one page, put the ids on each container and call init once with the key.

Several placements
html
<div id="gen-sdk-1" class="gen-sdk-class" data-placement-id="{{PLACEMENT_ID_1}}"
     data-style-id="{{STYLE_ID_1}}" style="width: 100%; height: 600px;"></div>
<div id="gen-sdk-2" class="gen-sdk-class" data-placement-id="{{PLACEMENT_ID_2}}"
     data-style-id="{{STYLE_ID_2}}" style="width: 320px; height: 600px;"></div>
<script src="https://media.begenuin.com/sdk/2.0.6/gen_sdk.min.js"></script>
<script>
  window.genuin.init({ api_key: "<<YOUR_API_KEY>>" });
</script>

Step 4: Send the signals the placement expects

Each signal ticked on the Implement tab maps to a key in contextual_params. Send the ones your page knows; the SDK adds the page URL itself. On a single-page app, call update with the container id when the route changes.

Signals
js
window.genuin.init({
  placement_id: "{{PLACEMENT_ID}}",
  style_id: "{{STYLE_ID}}",
  api_key: "<<YOUR_API_KEY>>",
  contextual_params: {
    page_context: "running shoes, trail, waterproof",
    geo: { lat: 47.6062, long: -122.3321 },
    user_segments: { gender: "F", min_age: 25, max_age: 34 },
  },
});

Each key also has a data attribute for the multi-container pattern; objects and arrays go in as JSON strings.

Implement signalcontextual_params keyData attribute
Custom Contextpage_context, user_contextdata-page-context, data-user-context
Previous Page Contextprevious_page_contextdata-previous-page-context
Locationgeo with lat, long, radius_limitdata-lat and data-long, or data-geo as JSON
Placeplace with country, state, city, zipcodedata-place as JSON
Timetimedata-time
User Segmentationuser_segments with age, min_age, max_age, segment, gender, race, interestsdata-user-segments as JSON
User Interestsuser_interests, an array of stringsdata-user-interests as JSON
Product Detail Page URLurl; defaults to the current page URLdata-url
Brand IDbrands_ids, an array of numbersdata-brands-ids as JSON
Community & Group IDcommunity_ids, loop_ids (group ids), arrays of stringsdata-community-ids, data-loop-ids as JSON
Posted By Userposted_by_user_ids, an array of stringsdata-posted-by-user-ids as JSON

What each signal does to the ranking is in Deliver contextual feeds with page, user, and location context and How contextual feeds choose videos.

Step 5: Override the style's titles and sections (Optional)

A style carries a title, a subtitle, and, for sectioned styles, a list of sections with their own titles and descriptions. The configuration option replaces those values for one container at render time. One placement can then serve a show page, a campaign page, or a category page, each with its own headings. Section descriptions also steer which videos each section gets.

Overrides
js
window.genuin.init({
  placement_id: "{{PLACEMENT_ID}}",
  style_id: "{{STYLE_ID}}",
  api_key: "<<YOUR_API_KEY>>",
  configuration: {
    style_title: "Trail running",
    style_subtitle: "Picked for this week's routes",
    sections: [
      { title: "Shoe reviews", description: "trail running shoe reviews and fit tests" },
      { title: "Technique", description: "downhill form, cadence, and pacing drills" },
    ],
  },
});

Sections are matched by position, so pass them in the style's order. Sections whose description in Brand Control Center starts with @ take the description you pass; the others keep their own.

Implement in a framework

Load the script as shown for embeds in Embed the Genuin Web SDK on your website; only the container changes. Each snippet below is the placement container for that framework.

Plain HTML

index.html
html
<div id="gen-sdk" class="gen-sdk-class"
     data-placement-id="{{PLACEMENT_ID}}" data-style-id="{{STYLE_ID}}"
     data-api-key="<<YOUR_API_KEY>>" data-page-context="running shoes, trail"
     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({});</script>

React

GenuinPlacement.tsx
tsx
import { useEffect } from "react";

const SDK_URL = "https://media.begenuin.com/sdk/2.0.6/gen_sdk.min.js";

export function GenuinPlacement({ pageContext }: { pageContext: string }) {
  useEffect(() => {
    if (document.querySelector(`script[src="${SDK_URL}"]`)) {
      window.genuin?.init({});
      return;
    }
    const script = document.createElement("script");
    script.src = SDK_URL;
    script.async = true;
    script.onload = () => window.genuin?.init({});
    document.body.appendChild(script);
  }, []);

  useEffect(() => {
    window.genuin?.update({ container_id: "gen-sdk", contextual_params: { page_context: pageContext } });
  }, [pageContext]);

  return (
    <div id="gen-sdk" className="gen-sdk-class" data-placement-id="{{PLACEMENT_ID}}"
         data-style-id="{{STYLE_ID}}" data-api-key="<<YOUR_API_KEY>>"
         data-page-context={pageContext} style={{ width: "100%", height: 600 }} />
  );
}

The second effect pushes a new page_context to the running placement when the prop changes. The window.genuin type declaration from the embed page applies here too.

Next.js

app/components/genuin-placement.tsx
tsx
"use client";
import Script from "next/script";

export function GenuinPlacement({ pageContext }: { pageContext: string }) {
  return (
    <>
      <div id="gen-sdk" className="gen-sdk-class" data-placement-id="{{PLACEMENT_ID}}"
           data-style-id="{{STYLE_ID}}" data-api-key="<<YOUR_API_KEY>>"
           data-page-context={pageContext} style={{ width: "100%", height: 600 }} />
      <Script
        src="https://media.begenuin.com/sdk/2.0.6/gen_sdk.min.js"
        strategy="afterInteractive"
        onLoad={() => window.genuin?.init({})}
      />
    </>
  );
}

Render the component from a server component and pass the page's context as a prop. On client-side navigation, onLoad doesn't fire again, so also call window.genuin?.init({}) from a useEffect.

Angular

genuin-placement.component.ts
ts
import { AfterViewInit, Component, Input } from "@angular/core";

@Component({
  selector: "genuin-placement",
  standalone: true,
  template: `<div id="gen-sdk" class="gen-sdk-class"
    data-placement-id="{{PLACEMENT_ID}}" data-style-id="{{STYLE_ID}}"
    data-api-key="<<YOUR_API_KEY>>" [attr.data-page-context]="pageContext"
    style="width: 100%; height: 600px"></div>`,
})
export class GenuinPlacementComponent implements AfterViewInit {
  @Input() pageContext = "";

  ngAfterViewInit(): void {
    const src = "https://media.begenuin.com/sdk/2.0.6/gen_sdk.min.js";
    const genuin = () => (window as any).genuin;
    if (document.querySelector(`script[src="${src}"]`)) { genuin()?.init({}); return; }
    const script = document.createElement("script");
    script.src = src;
    script.async = true;
    script.onload = () => genuin()?.init({});
    document.body.appendChild(script);
  }
}

Layout styles

The placement's layout is set in Brand Control Center, not in code; the SDK renders whatever the style says.

LayoutWhat visitors seeSet in Brand Control Center
CarouselA horizontal row of tiles to swipe through, in the default or focus stylePlacement layout, Carousel Style
FeedA vertical, swipeable column of videosPlacement layout, Scroll Behavior of free scroll or paging
GridRows and columns of tiles, up to 3 by 3, with optional auto-adjustPlacement layout, Grid layout
DynamicSections derived from the page, each a row of videos; a single "Latest Highlight" row until the sections are readyPlacement layout; the dynamic algorithm is enabled per placement by Genuin

Any layout can have sections: titled groups of videos, each filled by its own search. A section shows its title, subtitle, cover, thumbnail, and clip count as the style's Section Details allow.

Placement layout and Placement Size are style settings; the SDK reads them at load time.

Customize the placement

These options are specific to placements or behave differently from embeds. Everything else, including token, params, start_video_slug, targeting, and error_handler, is shared and listed in Embed the Genuin Web SDK on your website.

ParameterTypeRequiredDefaultDescription
placement_idstringYesnoneThe placement to render. Data attribute: data-placement-id. Takes precedence over embed_id on the same container.
style_idstringYesnoneThe style whose layout and sections to use. Data attribute: data-style-id.
contextual_paramsobjectNononeThe signals declared on the Implement tab; see Step 4. url defaults to the current page URL.
configurationobjectNothe style's own valuesstyle_title, style_subtitle, and sections with title, subtitle, description, cover_url, thumbnail_url. See Step 5.
theme"light" or "dark"No"light"Colour theme of the player. Data attribute: data-theme.
Container widthCSSNothe style's Placement Size widthSet width on the container. The style's width and height, and Auto-fit Height, size the player inside it; the SDK then sets the container height to the rendered content.
Font——Inter, from Google FontsNot configurable in 2.0.6. The player renders in a Shadow DOM, so page CSS doesn't reach it.
sponsorship_idstring[]Nothe placement's own sponsorshipOverrides the sponsorship attached to the placement for ad delivery.

Troubleshooting

SymptomCauseFix
Console error Placement ID or Style ID is missing, and no embed ID providedOnly one of placement_id and style_id was setPass both, in init or as data-placement-id and data-style-id.
No embed details found for placement configuration.The placement id doesn't exist, or the style id isn't one of this placement's stylesCopy both ids again from the Implement tab.
The embed renders instead of the placementA data attribute is misspelt, so the SDK saw an embed_id onlyCheck data-placement-id and data-style-id on the container.
Sections show the style's titles, not yoursconfiguration.sections is in a different order, or the section's description in Brand Control Center doesn't start with @Match the style's section order; mark overridable descriptions with @.
Context changes have no effectupdate was called without container_idPass the container's id.
A dynamic placement shows one "Latest Highlight" rowSections for this page are still being generated, or the dynamic algorithm isn't enabled for the placementReload after a minute; ask Genuin to enable the algorithm for the placement.

Limitations

  • Both ids, always: A placement needs placement_id and style_id; there's no default style.
  • Layout lives in Brand Control Center: Code can't switch a placement between carousel, feed, grid, and dynamic.
  • Grid size: Up to 3 columns and 3 rows.
  • Dynamic layout: Sections are generated per page URL and context and cached for 15 minutes; a new page shows the default row first.
  • Section overrides by position: configuration.sections is matched to the style's sections in order, and only descriptions marked @ are replaced.
  • One brand per page: As with embeds, all placements on a page belong to one brand.
  • Font and CSS: The player's styles are isolated; the font is fixed to Inter in 2.0.6.

See also

TRUST AND COMPLIANCE

© 2026 Genuin Inc.

Genuin Footer