IdukkiIdukki
Developers

Embed

Embed the widget and talk to it from the page

One placeholder element, one script. The loader finds every embed on the page, prefetches its data and mounts the 37 KB runtime. This page covers the attributes the loader reads, the window.idukki surface, the events it emits and the mobile WebView bridge.

The snippet

Copy the widget GUID and business GUID from the dashboard (Widget Settings, Embed Code). The data-ugc="idukki" attribute is what the loader searches for; data-guid selects the widget and data-bguid scopes it to your business so the data request hits the cached per-business path.

Any HTML pagehtml
<!-- Where the gallery should render -->
<div
  data-ugc="idukki"
  data-guid="YOUR_WIDGET_GUID"
  data-bguid="YOUR_BUSINESS_GUID"
  data-app="true"
></div>

<!-- Once per page -->
<script src="https://assets.idukki.io/version/script_0" async></script>

Several embeds on one page are fine: the loader iterates every [data-ugc] element and prefetches each one, keyed by widget, product and personalisation state, so two embeds of the same widget share one request.

Attributes the loader and runtime read

AttributePurpose
data-ugc="idukki"Marks the element as an Idukki mount point. Required.
data-guidWidget (gallery layout) GUID. Required.
data-bguidBusiness GUID. Routes the data request through the per-business cached path. Use it.
data-app="true"Set by the dashboard snippet; marks a standard app embed.
filter-pidProduct id(s), comma-separated, on a product page. The runtime renders only posts tagged with those products and fires pdp_no_content when there are none. Can be set after mount: a MutationObserver re-fetches when it changes.
filter-pcatProduct category filter, same semantics as filter-pid.
data-auto-trigger="false"Skip the loader prefetch and the automatic mount for this element.
data-retargetEnables the visited-products and wishlist boost for this embed. "", "false", "0" or absent means off; no retarget tag leaves the browser in that state.
data-idk-eager="1"Inject the runtime immediately instead of on first scroll-in.
data-idk-lite="1"Request the lite bundle for that embed.
data-idk-app="1"Turn on the mobile app bridge (see below).
data-idukki-collectNot a gallery: mounts the post-purchase UGC collection form. Also available as window.idukkiCollection.mount(el, { bguid, productId }).

window.idukki

The runtime installs a small global before React mounts. It exists so a merchant script can report a conversion without waiting for the widget, for example on a thank-you page that loads before any gallery does.

Report a purchase from the thank-you pagejs
// Safe to call before the widget has mounted: calls are queued and
// replayed once an experiment is active.
window.idukki = window.idukki || { _isStub: true, _queue: [], track: function () { this._queue.push(arguments) } };
window.idukki.track('purchase', 149.0);
MemberWhat it does
idukki.track(event, revenue?)Records a conversion event against the visitor’s current A/B assignment. Before an experiment activates it is a queueing stub (_isStub: true); activateExperiment() swaps in the real tracker and replays _queue.
idukki.abDiagnostic only: the resolved experiment assignment (experiment id, variant, holdout flag, source). Logged to the console as [idukki A/B]; append ?idk_debug=1 to the page URL for an on-page toast.
idukkiCollection.mount(el, { bguid, productId })Mounts the post-purchase collection form into el. Lazy: the form chunk is fetched on first mount.

Events the widget emits

Every event Idukki’s own sinks receive carries the same contract fields: businessId, layoutId, postId and productId where relevant, and widgetVersion. The GA4 and Meta mirror uses the same camelCase keys inside props.

SinkEvents
POST /analytics/post-analyticsPer-post impressions, deduplicated per post, per layout, per browser-tab session. Nothing is sent when nothing new was seen.
POST /analytics/clicksclick, PDP_REDIRECT, add_to_cart, widget_impression (once per business, gallery and page per load), impression and ab_visitor (A/B exposure and assignment), pdp_no_content, personalisation events.
GA4 / Meta (merchant’s own tags)ugc_widget_view, gallery_impression, gallery_click, select_item, view_item, video_play, add_to_cart, and ViewContent, AddToCart, Lead on Meta. Custom GA4 names take the business prefix (idukki_ by default); reserved ecommerce names are never prefixed.

Add-to-cart from the widget stamps _idk_vid, _idk_biz, _idk_exp and _idk_var as Shopify cart attributes; the order webhook reads them back as note_attributes and that is what reconciles a sale to a post. See the webhooks page.

React, Next.js and other frameworks

The runtime mounts into the placeholder and never touches your app’s state. Render the element, inject the script once, and keep both in a leaf client component so server rendering has nothing to hydrate.

Next.js App Routertsx
'use client'
import Script from 'next/script'

export function IdukkiGallery({ guid, bguid, productId }: { guid: string; bguid: string; productId?: string }) {
  return (
    <>
      <div data-ugc="idukki" data-guid={guid} data-bguid={bguid} data-app="true" filter-pid={productId} />
      <Script src="https://assets.idukki.io/version/script_0" strategy="lazyOnload" />
    </>
  )
}

Mobile apps and WebViews

The widget runs in any WebView. If the app shows your website, the normal embed works once DOM storage and window.open handling are enabled. For native screens with no website, point a WebView at the hosted page and turn on the bridge.

Hosted widget pagetext
https://widget.idukki.io/?guid=<WIDGET_GUID>&bguid=<BUSINESS_GUID>&idk_app=1

With the bridge on, every outbound tap (product, Shop Now, share) is delivered to the app as { source: "idukki", type: "open", url, kind } instead of a popup: via ReactNativeWebView.postMessage, the idukki WKWebView message handler, an IdukkiAndroid JavaScript interface, or a window CustomEvent named idukki:open. Append idk_appcheck=1 to the URL to see a diagnostic panel inside the app.

Performance contract

  • Runtime: 37 KB. Media is lazy-loaded; video preload is explicit per tile, auto only for the active slide of a carousel and never more than three at load on the reference page.
  • Data requests go through widget-api.idukki.io, a CloudFront path with a version-aware ETag, so a repeat visit answers 304 until you publish a change.
  • No layout shift on mount: the loader reserves the placeholder before the runtime arrives.

Checked against: idukki-ext/src/loader.js, idukki-ext/src/main.jsx, idukki-ext/src/experiments/abTesting.ts, idukki-ext/docs/APP_INTEGRATION.md, idukki-ext/docs/ANALYTICS_EVENTS.md. Something here that the product does not do? Tell us and it gets fixed in the doc or the code.

We use cookies

We use essential cookies to run this site and optional analytics cookies to understand how it’s used. You can change your choice anytime in our privacy policy.