SDK Start Here This page explains how to integrate @froomle/frontend-sdk from first setup to advanced usage. It covers both declarative integrations (data-froomle-* placeholders) and programmatic integrations (JS/TS API calls and React bindings). Use this page as a map: Choose architecture and integration style: Choose an Integration Pattern Follow a practical onboarding path: Learning Path and Beginner Flow Identify runtime-specific setup rules: Runtime Scope Guide and Script Loading and Initialization Modes Install and initialize the SDK: Install and Run and Core Configuration Add recommendation auth when required: Recommendation Authentication Build recommendation blocks with markup: Recommendations in Markup Fetch and render recommendations in code: Programmatic API (JS/TS) Integrate in React: React Bindings Find runnable end-to-end examples: Sample Projects Debug setup and runtime issues: Troubleshooting and Validation Want working end-to-end examples instead of isolated snippets? See frontend-sdk-examples for full sample projects, including plain HTML, React, TypeScript, and ArcXP setups. Choose an Integration Pattern Mode Integration style Use when Declarative HTML attribute-centric (data-froomle-*) You want SDK-driven DOM population from placeholders. Works for plain HTML and SSR templates (including JSP-generated markup). Declarative JS/TS bootstrap + HTML attributes You initialize SDK in JS/TS (setEnvironment, setPageVisit, …), while recommendation blocks are still mapped with data-froomle-*. Programmatic JS/TS API-centric You fetch recommendations directly in code with getRecommendations or proxyReco and render your own UI. Programmatic Component-centric (React bindings) You use @froomle/frontend-sdk/react (useCreateReco, FroomleReco, FroomleOrder, …) to compose recommendation UI in React. JSP + TS is the same integration model as JS/TS bootstrap + HTML attributes. The main difference is only where markup is rendered (server template vs client template generation). Event Tracking by Implementation Choice Implementation choice Auto event tracking What you need to do Declarative (data-froomle-*) Yes The SDK auto-tracks standard recommendation impressions/clicks for rendered blocks. React bindings (@froomle/frontend-sdk/react) Yes (when using SDK React rendering primitives) Use SDK components/hooks for standard tracking behavior. For fully custom rendering, use the manual event helpers (sendAddToCart(…), sendPurchase(…), etc.) or the generic sendEvent(…) fallback. Programmatic (JS/TS API) No Send events manually with the dedicated helper functions for supported flows, or use sendEvent(…) as the low-level fallback. Follow Tracking events. Learning Path: Beginner to Advanced Use this sequence if you are new to the SDK: Start with one script tag and one recommendation placeholder. Add detail-page context (context_item) for article/product pages. Add filters and list variables for targeting. Add consent, identity, and histories. Move to module-based setup (npm import) for production frontend integration. Use programmatic APIs when you need direct request/render control. Use React bindings when recommendations are composed from React components. Beginner Flow (First Working Integration) Minimum concept: Configure environment and page visit. Add one recommendation placeholder with field mappings. Set consent. Verify /recommendations/requests in browser network logs. If these four steps work, the rest of the SDK features are incremental extensions (filters, context item, histories, ordering, programmatic calls, React helpers). Once you want a full runnable project instead of the minimal first integration, use frontend-sdk-examples. Install and Run Script-Tag Setup (No Build Tool) Recommended starter (default defer mode): <script> window.addEventListener('froomle:before-init', function (event) { var sdk = event.detail && event.detail.sdk if (!sdk) return sdk.setConsent(2) }, { once: true }) </script> <script defer src="https://cdn.jsdelivr.net/npm/@froomle/frontend-sdk@latest/dist/froomle.global.js" data-froomle-env="your_env" data-froomle-page-visit="home" ></script> Use froomle:before-init for settings that must be present on the first recommendation request. Need a runnable example instead of the minimal snippet above? Use frontend-sdk-examples and start with the plain HTML examples. Runtime Scope Guide Use this quick map to choose the correct sections for your integration runtime: Runtime Sections to use Notes Script-tag/global (froomle.global.js) Script-Tag Setup, Script Loading and Initialization Modes, Script-tag Whitelist Uses HTML script attributes and init hooks (froomle:before-init, froomle:init). Module/import (JS/TS bundles) Module Setup, Core Configuration, Programmatic API (JS/TS) Use imported setters (setEnvironment, setPageVisit, setConsent, …). If you render declarative data-froomle-* placeholders from a module/import setup, call init() explicitly after those setters. React bindings Module Setup, React Bindings, Core Configuration React apps use module/import setup. Script-tag init hooks do not apply. Shared (all runtimes) Core Configuration, Recommendations in Markup, Programmatic API (JS/TS) Setting semantics are shared across runtimes. Only bootstrap/initialization entrypoint differs. Shared Runtime State vs Recommendation Block Configuration Across runtimes, distinguish between shared SDK runtime state and per-block or per-request configuration. Common page/app-level runtime state is typically set once for the current page context: setEnvironment(…) setPageVisit(…) setChannel(…) setConsent(…) setDeviceId(…) setUserId(…) setContextItem(…) setContextItemType(…) Optional runtime-wide overrides such as setRequestDomain(…), setSafeRequest(…), setRecommendationsBearer(…), and setRecommendationsBearerProvider(…) are also configured centrally when your integration requires them. If a page contains multiple recommendation blocks, reuse that shared SDK state across all of them. If the page context changes in an SPA or hybrid navigation flow, update the page-level setters once for the new route/context. Per-block or per-request configuration is usually supplied separately for each recommendation block or API call: recommendation list name, limit, or list_size block-specific filters and variables recommendation request arguments passed to getRecommendations(…), useCreateReco(…), or useRecoList(…) block-specific rendering and layout In script-tag integrations, shared runtime state usually lives on the SDK script tag or in froomle:before-init, while per-block configuration lives on the recommendation placeholders. In React and programmatic integrations, shared runtime state is typically initialized once in the app/bootstrap layer, while each recommendation block or API call provides its own list/filter/rendering configuration. Script Loading and Initialization Modes (Global Script Tag) Default for script-tag integrations: use defer. This section applies to global/script-tag usage (froomle.global.js). For React and other module/import integrations, use module setup (setEnvironment, setPageVisit, setConsent) and ignore script loading modes. Mode Use when Key requirement defer (default) Most websites and CMS templates. Register first-request JS setters in froomle:before-init; use froomle:init or window.FroomleFrontendSdkReady for post-init code. blocking Legacy pages that require strict synchronous script order. Keep the SDK tag without defer/async; if first-request values are set from JS, still register froomle:before-init before the SDK tag. async Startup order with other scripts is not strict. Use froomle:before-init for first-request values and froomle:init/window.FroomleFrontendSdkReady for post-init logic. Event-driven Complex frontends with multiple modules/microfrontends. Use froomle:before-init for pre-init settings and froomle:init for post-init orchestration; keep the ready promise as late-safe fallback. Hook Timing and Responsibilities froomle:before-init: pre-init hook, fired before DOM init and before the first recommendation request. froomle:init: post-init hook, fired once when SDK initialization completes. window.FroomleFrontendSdkReady: sticky readiness promise for consumers that may attach after events already fired. When to use each hook: Use froomle:before-init for settings that must be included in the first request: consent, custom variables, histories, dynamic channel, runtime identity, or recommendation auth. Use froomle:init for code that depends on completed initialization (module orchestration, UI hooks, diagnostics). Use window.FroomleFrontendSdkReady as a reliable fallback for late-loaded bundles/modules. Settings Matrix (Script-Tag Mode) Use this matrix as the canonical checklist for what to configure where. This matrix is only for script-tag/global integrations (froomle.global.js). React/module-import integrations do not use these hooks. Setting Script tag attribute? Must be set before first request? Recommended place and usage environment (setEnvironment) Yes: data-froomle-env Yes (required) Put on SDK <script> tag. page_visit (setPageVisit) Yes: data-froomle-page-visit Yes (required) Put on SDK <script> tag. request_domain (setRequestDomain) Yes: data-froomle-request-domain Yes (if custom domain is needed) Use script tag for fixed value; use froomle:before-init if runtime-derived. safe_request (setSafeRequest) Yes: data-froomle-safe-request Yes (if changed from default) Use script tag for fixed value; use froomle:before-init for runtime decisions. recommendation bearer (setRecommendationsBearer) No Yes (if the first recommendation request requires auth) Set in froomle:before-init when your backend already rendered a short-lived token into the page. recommendation bearer provider (setRecommendationsBearerProvider) No Yes (if the first recommendation request requires auth) Set in froomle:before-init when the browser should fetch or refresh a token from your backend. context_item / context_item_type Yes: data-froomle-context-item, data-froomle-context-item-type Yes (on detail pages) Use script tag for fixed detail pages, or set in froomle:before-init. user_id (setUserId) Yes: data-froomle-user-id Yes (if first request must be identified) Fixed value: script tag. Runtime value (cookie/session): froomle:before-init. device_id (setDeviceId) Yes: data-froomle-device-id Yes (if first request must carry device identity) Fixed value: script tag. Runtime value: froomle:before-init. channel (setChannel) Yes: data-froomle-channel Yes (if channel affects first request) Fixed channel: script tag. Dynamic channel (viewport/device): froomle:before-init. consent (setFroomleConsent / sdk.setConsent) No Yes (if first request must respect consent level) Set in froomle:before-init. Script-tag alias: setFroomleConsent(level). Namespaced equivalent: event.detail.sdk.setConsent(level). custom variables (setCustomVariable) No Yes, if variable must affect first request First-request variables: froomle:before-init. Late/non-critical variables: froomle:init or window.FroomleFrontendSdkReady. histories (addHistories) No Yes, if histories must influence first request Set in froomle:before-init. Post-init orchestration (non-settings) N/A No Use froomle:init and/or window.FroomleFrontendSdkReady for UI wiring, telemetry, and late modules. <script> window.addEventListener('froomle:before-init', function (event) { var sdk = event.detail && event.detail.sdk if (!sdk) return sdk.setConsent(2) sdk.setCustomVariable('section', 'homepage') }, { once: true }) window.addEventListener('froomle:init', function (event) { var sdk = event.detail && event.detail.sdk if (!sdk) return // Post-init code }) </script> defer (default) <script> window.addEventListener('froomle:before-init', function (event) { var sdk = event.detail && event.detail.sdk if (!sdk) return sdk.setConsent(2) sdk.setCustomVariable('section', 'homepage') }, { once: true }) </script> <script defer src="https://cdn.jsdelivr.net/npm/@froomle/frontend-sdk@latest/dist/froomle.global.js" data-froomle-env="your_env" data-froomle-page-visit="home" ></script> <script> window.FroomleFrontendSdkReady.then(function () { // Post-init code }) </script> blocking (legacy compatibility) <script> window.addEventListener('froomle:before-init', function (event) { var sdk = event.detail && event.detail.sdk if (!sdk) return sdk.setConsent(2) }, { once: true }) </script> <script src="https://cdn.jsdelivr.net/npm/@froomle/frontend-sdk@latest/dist/froomle.global.js" data-froomle-env="your_env" data-froomle-page-visit="home" ></script> <script> window.FroomleFrontendSdkReady.then(function () { // Post-init code }) </script> async (supported with readiness gating) <script> window.addEventListener('froomle:before-init', function (event) { var sdk = event.detail && event.detail.sdk if (!sdk) return sdk.setConsent(2) }, { once: true }) </script> <script async src="https://cdn.jsdelivr.net/npm/@froomle/frontend-sdk@latest/dist/froomle.global.js" data-froomle-env="your_env" data-froomle-page-visit="home" ></script> <script> window.FroomleFrontendSdkReady.then(function () { // Post-init code }) </script> Event-driven <script> window.addEventListener('froomle:before-init', function (event) { var sdk = event.detail && event.detail.sdk if (!sdk) return sdk.setConsent(2) sdk.setCustomVariable('init_source', 'before-init') }, { once: true }) window.addEventListener('froomle:init', function (event) { var sdk = event.detail && event.detail.sdk if (!sdk) return // Post-init code }) </script> <script async src="https://cdn.jsdelivr.net/npm/@froomle/frontend-sdk@latest/dist/froomle.global.js" data-froomle-env="your_env" data-froomle-page-visit="home" ></script> <script> window.FroomleFrontendSdkReady.then(function () { // Late-safe fallback }) </script> froomle:before-init and froomle:init event payload: Field Meaning event.detail.sdk SDK API object available at init time. event.detail.mode Init trigger mode: auto or manual. event.detail.version SDK version string when available. mode values: auto: the SDK started itself during script evaluation (standard behavior in script-tag usage). manual: the SDK was started via init(). In normal script-tag integrations, you will usually see mode=auto. Module Setup (JS/TS/React/JSP Bundles) npm install @froomle/frontend-sdk Initialize shared SDK state in code: import { setEnvironment, setPageVisit, setConsent } from '@froomle/frontend-sdk' setEnvironment('your_env') setPageVisit('home') setConsent(2) Module/import setup has two valid bootstrap shapes: React and purely programmatic JS/TS integrations do not call init(). If your module/import integration also uses declarative data-froomle-* placeholders, call init() explicitly after the shared SDK setters to start DOM placeholder mode. Consent Setter Name by Runtime Use the consent setter name that matches how the SDK is loaded: Runtime mode Consent call Script-tag/global (froomle.global.js) setFroomleConsent(level) or window.FroomleFrontendSdk.setConsent(level) Module/import (npm install @froomle/frontend-sdk) setConsent(level) from import { setConsent } … Local Development For static HTML files, avoid file:// and run a local server: python3 -m http.server 8080 For module projects, run your framework/bundler dev command (for example npm run dev or npm run serve). Core Configuration This section defines setting semantics shared across runtimes. For setter scope (shared page/app runtime state vs per-block or per-request configuration), use Runtime Scope Guide. For script-tag timing and hook placement (what must be set before the first request), use Script Loading and Initialization Modes and Supported Inputs by Location. React follows module/import setup and does not use script-tag init hooks. Setter Reference and Defaults Setting Default Script-tag attribute Typical use setEnvironment(env) null data-froomle-env Always set to your environment. setPageVisit(pageType) null data-froomle-page-visit Always set for recommendation requests. setConsent(level) 0 (none) In module/import setups, use setConsent(level). In script-tag/global setups, use setFroomleConsent(level). setDeviceId(id) no-consent data-froomle-device-id Optional override when you manage device IDs yourself. setUserId(id) null data-froomle-user-id Use for stable logged-in identity. setRequestDomain(host) europe-west1.froomle.com data-froomle-request-domain Override domain (custom API domain/proxy/local setup). setRecommendationsBearer(token) (none) (none) Add Authorization: Bearer … to recommendation requests only. Use when your backend already injected a short-lived token. clearRecommendationsBearer() (none) (none) Clear the cached recommendation bearer token, for example on logout or tenant switch. setRecommendationsBearerProvider(provider) (none) (none) Async provider for recommendation requests only. The SDK waits for it before the first request and retries once after a 401 with forceRefresh: true. clearRecommendationsBearerProvider() (none) (none) Remove the active recommendation token provider, for example on logout or tenant switch. setChannel(channel) www-desktop data-froomle-channel Force channel or set responsive channel in runtime code. setSafeRequest(bool) true data-froomle-safe-request Set to false for non-HTTPS local/dev domains. setContextItem(itemId) null data-froomle-context-item Detail pages. setContextItemType(type) article data-froomle-context-item-type Detail pages with non-default type. setCustomVariable(key, value) (none) (none) Request-root variables and custom business data. addHistories(histories, defaultItemType?) empty (none) Add history IDs or typed history entries ({ id, item_type } or { id, itemType }). Supported Inputs by Location Location Runtime scope Inputs What This Is For SDK script tag Script-tag/global only data-froomle-env data-froomle-page-visit data-froomle-context-item data-froomle-context-item-type data-froomle-request-domain data-froomle-device-id data-froomle-user-id data-froomle-safe-request data-froomle-channel Bootstraps SDK from HTML without imports. Only this whitelist is read from the script tag. Recommendation auth is configured via JS setters, not script-tag attributes. Recommendation markup All runtimes data-froomle-reco data-froomle-param-* data-froomle-variable-* data-froomle-reco-filter-* ordering attributes: data-froomle-order-*, data-froomle-ordervalue Declarative recommendation rendering in the DOM. Use this to map item fields into existing HTML placeholders. JS/TS module API Module/import runtimes setEnvironment setPageVisit setConsent setDeviceId setUserId setRequestDomain setSafeRequest setChannel setRecommendationsBearer clearRecommendationsBearer setRecommendationsBearerProvider clearRecommendationsBearerProvider setContextItem setContextItemType setCustomVariable addHistories init getRecommendations proxyReco fulfillRecommendations sendItemInteraction sendUserInteraction sendAddToCart sendRemoveFromCart sendPurchase sendEvent Code-first integration. Use this for explicit runtime control, programmatic fetching, and manual event flows. If you also render declarative data-froomle-* placeholders from a module/import setup, call init() explicitly to start DOM placeholder mode. React bindings React only FroomleSdkInit useCreateReco FroomleReco useReco FroomleOrder FroomleOrderItem FroomleCustomItem React component/hook layer on top of core SDK APIs. Use this when recommendations are composed directly in React components. Programmatic Method Quick Reference Method Purpose getRecommendations(lists) Fetch recommendation lists directly in code. proxyReco(request) Queue a recommendation request and get a promise-like recommendation handle. fulfillRecommendations() Flush queued proxy requests in one batched API call. sendEvent(actionItem, actionItemType, eventType?, extras?) Low-level fallback for explicit custom/manual tracking events. sendItemInteraction(actionItem, actionItemType, interactionType, extras?) sendItemInteraction(interactionType, extras?) Send item_interaction for flows such as wishlists, likes, or favorites. The one-argument form reuses the current context_item / context_item_type. sendUserInteraction(interactionType, extras?) Send user_interaction for non-item flows such as review submits or chat starts. sendAddToCart(actionItem, actionItemType, amount, extras?) sendAddToCart(amount, extras?) Send add_to_cart with required amount and optional basket_content. The short form reuses the current context_item / context_item_type. sendRemoveFromCart(actionItem, actionItemType, amount, extras?) sendRemoveFromCart(amount, extras?) Send remove_from_cart with required amount and optional basket_content. The short form reuses the current context_item / context_item_type. sendPurchase(actionItem, actionItemType, amount, purchasedPrice, extras?) sendPurchase(amount, purchasedPrice, extras?) Send purchase with required amount and purchased_price. The short form reuses the current context_item / context_item_type. setRecommendationsBearer(token) Configure a static recommendation bearer token. setRecommendationsBearerProvider(provider) Configure an async recommendation token provider. clearRecommendationsBearer() Clear the cached recommendation bearer token. clearRecommendationsBearerProvider() Clear the configured recommendation token provider. TypeScript Types and Interfaces The SDK publishes declaration files via package exports: @froomle/frontend-sdk → dist/index.d.ts @froomle/frontend-sdk/react → dist/react.d.ts Typical TS imports: import { addHistories, clearRecommendationsBearer, clearRecommendationsBearerProvider, fulfillRecommendations, getRecommendations, proxyReco, sendAddToCart, sendEvent, sendItemInteraction, sendPurchase, sendRemoveFromCart, sendUserInteraction, setConsent, setRecommendationsBearer, setRecommendationsBearerProvider, setDeviceId, setEnvironment, setPageVisit } from '@froomle/frontend-sdk' import type { RecommendationItem, Recommendations } from '@froomle/frontend-sdk' The package exports RecommendationItem and Recommendations as types. Some helper request/input shapes are currently inferred from function signatures instead of exported as named types. The following shapes mirror current SDK declarations: type RecoRequestShape = { list: string filters: Array<{ key: string; value: string }> others?: Array<{ key: string; value: unknown }> } type HistoryInputShape = | string | { id: string; item_type?: string | null; itemType?: string | null } type SendEventExtrasShape = { [key: string]: string | number | object } type BasketContentItemShape = { id: string [key: string]: string | number | object | undefined } Current signature highlights from the published declarations: setConsent(consent: number): void setUserId(userId: string): void setDeviceId(deviceId: string): void setRecommendationsBearer(token: string | null | undefined): void setRecommendationsBearerProvider(provider: (options: { forceRefresh: boolean }) => string | Promise<string>): void setCustomVariable(key: string, value: unknown): void getRecommendations(lists: Array<{ limit: number list_name: string list_size: number [key: string]: unknown }>): Promise<Recommendations> sendItemInteraction( actionItem: string, actionItemType: string, interactionType: string, extras?: { [key: string]: string | number | object | undefined } ): void sendItemInteraction( interactionType: string, extras?: { [key: string]: string | number | object | undefined } ): void sendUserInteraction( interactionType: string, extras?: { [key: string]: string | number | object | undefined } ): void sendAddToCart( actionItem: string, actionItemType: string, amount: number, extras?: { basket_content?: BasketContentItemShape[] [key: string]: string | number | object | undefined } ): void sendAddToCart( amount: number, extras?: { basket_content?: BasketContentItemShape[] [key: string]: string | number | object | undefined } ): void sendRemoveFromCart( actionItem: string, actionItemType: string, amount: number, extras?: { basket_content?: BasketContentItemShape[] [key: string]: string | number | object | undefined } ): void sendRemoveFromCart( amount: number, extras?: { basket_content?: BasketContentItemShape[] [key: string]: string | number | object | undefined } ): void sendPurchase( actionItem: string, actionItemType: string, amount: number, purchasedPrice: number, extras?: { original_price?: number [key: string]: string | number | object | undefined } ): void sendPurchase( amount: number, purchasedPrice: number, extras?: { original_price?: number [key: string]: string | number | object | undefined } ): void useCreateReco(req: { list: string filters: Array<{ key: string; value: string }> [key: string]: any }): RecommendationItem & PromiseLike<RecommendationItem> unknown is intentional for pass-through/custom payload values (for example setCustomVariable values and extra list keys in getRecommendations list entries). This keeps SDK typings strict without guessing backend-specific business fields. Environment and Page Visit (Required) Set both before requesting recommendations. Script-tag setup: <script src="https://cdn.jsdelivr.net/npm/@froomle/frontend-sdk@latest/dist/froomle.global.js" data-froomle-env="sample_env" data-froomle-page-visit="home" ></script> Module setup: import { setEnvironment, setPageVisit } from '@froomle/frontend-sdk' setEnvironment('sample_env') setPageVisit('home') Identity: device_id and user_id Identity usage model: setUserId(…): set when the user has a stable logged-in ID. setDeviceId(…): set only when you manage device IDs yourself (or want to unify with your own device ID). For consent-driven identity behavior (user_id, device_id, and no-consent flows), see User identity and consent. user_id should be a real logged-in identifier or omitted. Never set user_id to "no-consent". If you do not set device_id, the SDK manages it automatically via froomle_device_id and generates it when consent allows tracking. For logged-in users, provide user_id; device_id can be SDK-managed or explicitly set by your own device-ID strategy. It should never happen that one device_id is associated with multiple user_id values in the same environment. Script-tag equivalents: <script src="https://cdn.jsdelivr.net/npm/@froomle/frontend-sdk@latest/dist/froomle.global.js" data-froomle-env="sample_env" data-froomle-page-visit="home" data-froomle-device-id="device-123" ></script> or <script src="https://cdn.jsdelivr.net/npm/@froomle/frontend-sdk@latest/dist/froomle.global.js" data-froomle-env="sample_env" data-froomle-page-visit="home" data-froomle-user-id="user-456" ></script> Identity and Storage (Cookies) In browser/script-tag usage, the SDK persists identity and consent in first-party cookies: froomle_device_id froomle_user_id froomle_consent Behavior: On load, the SDK reads existing cookie values and reuses them. If identity is passed via script attributes or setters, the SDK updates the matching cookie. Calling setFroomleConsent(…) (or window.FroomleFrontendSdk.setConsent(…)) updates froomle_consent. When consent becomes 2 and device_id is still no-consent, the SDK generates a UUID device id and stores it. Cookie properties in the current SDK build: first-party cookie path=/ samesite=lax long-lived max-age (20 years in current implementation) Older integrations may reference _fr_id; the current SDK implementation uses froomle_device_id. Consent Use setConsent(level) in modules. In script-tag integrations, setFroomleConsent(level) is the browser-global alias. setConsent(…) is not a bare global function in plain HTML. In script-tag pages, use setFroomleConsent(…) or window.FroomleFrontendSdk.setConsent(…). setConsent(2) // or, in plain HTML/global usage: setFroomleConsent(2) Level Name Recommendation requests Tracking events 0 No Tracking Anonymous request (device_id, user_group, version set to no-consent), no user_id, no histories Disabled 1 Analytics Only Same anonymous request as level 0 Enabled, but anonymous identity 2 Full Identified request (device_id, and user_id when set), histories allowed Enabled with identified identity Practical behavior: Consent 0: popular/non-personalized behavior, no tracked events. Consent 1: event tracking enabled, personalization identity still anonymous. Consent 2: full personalization and identified tracking when identity is set. Consent behavior Consent level Behavior 0 Requests remain anonymous, user_id and histories are excluded, and tracking events are not emitted. 1 Requests remain anonymous and omit histories; tracking events are emitted with anonymous identity. 2 Requests may include full identity and histories; tracking events are emitted with identified identity. Detail Context and Page Tracking For detail pages, set context item (and optional type): import { setContextItem, setContextItemType, setPageVisit } from '@froomle/frontend-sdk' setPageVisit('article_detail') setContextItem('sample_item_id_2') setContextItemType('article') Script-tag equivalent: <script src="https://cdn.jsdelivr.net/npm/@froomle/frontend-sdk@latest/dist/froomle.global.js" data-froomle-env="sample_env" data-froomle-page-visit="article_detail" data-froomle-context-item="sample_item_id_2" data-froomle-context-item-type="article" ></script> Behavior notes: context_item_type is optional; when omitted and context_item is set, SDK defaults it to article. The same context (context_item / context_item_type) is used for recommendation requests and detail tracking. When context_item_type is omitted, detail_pageview.action_item_type also defaults to article. When context_item is present, SDK emits detail_pageview for the page and suppresses the generic page_visit event. Request Domain, Channel, and Transport Use these when needed: setRequestDomain('your.domain') setSafeRequest(false) for non-HTTPS/local environments setChannel('www-desktop' | 'www-mobile') If you want to run SDK traffic on your own subdomain, follow Custom domain (CNAME) for DNS, SSL, and SDK setup steps. Responsive channel example: import { setChannel } from '@froomle/frontend-sdk' const channel = window.matchMedia('(max-width: 767px)').matches ? 'www-mobile' : 'www-desktop' setChannel(channel) Recommendation Authentication By default, browser SDK integrations do not need bearer auth for recommendation requests. Use the auth setters only when your recommendation API is configured to require a bearer token in the browser. Recommendation auth only affects recommendation requests. Tracked events (page_visit, detail_pageview, impression, click_on_recommendation) remain unauthenticated in the current SDK. Rules: Keep client_id and client_secret on your backend. Do not call /oauth/token from the browser. Configure auth before the first recommendation request. Use setRecommendationsBearer(token) when your backend already rendered a short-lived token into the page. Use setRecommendationsBearerProvider(async ({ forceRefresh }) ⇒ token) when the browser should ask your backend for a token. This section only covers how the frontend SDK receives and uses a bearer token. The actual OAuth token flow, client-credentials exchange, token refresh strategy, and backend responsibilities are documented in Authentication Flow. When to use which pattern: Static bearer: backend-rendered pages, SSR templates, or any flow where a valid token is already available synchronously. Provider: frontend apps that need to fetch a token from a minimal backend endpoint and refresh it after a 401. Static bearer in script-tag/global usage: <script> window.__FROOMLE_RECO_BEARER__ = '{{ server_rendered_short_lived_token }}' window.addEventListener('froomle:before-init', function (event) { var sdk = event.detail && event.detail.sdk if (!sdk) return sdk.setConsent(2) sdk.setRecommendationsBearer(window.__FROOMLE_RECO_BEARER__) }, { once: true }) </script> <script defer src="https://cdn.jsdelivr.net/npm/@froomle/frontend-sdk@latest/dist/froomle.global.js" data-froomle-env="your_env" data-froomle-page-visit="home" ></script> Static bearer in module and React usage: import { setConsent, setEnvironment, setPageVisit, setRecommendationsBearer } from '@froomle/frontend-sdk' setEnvironment('your_env') setPageVisit('home') setConsent(2) setRecommendationsBearer(window.__FROOMLE_RECO_BEARER__) Provider behavior: The SDK waits for the provider before the first recommendation request. If the recommendation request returns 401, the SDK calls the provider once more with forceRefresh: true. The SDK retries the recommendation request once with the refreshed token. Provider callback in script-tag/global usage: <script> window.addEventListener('froomle:before-init', function (event) { var sdk = event.detail && event.detail.sdk if (!sdk) return sdk.setConsent(2) sdk.setRecommendationsBearerProvider(async function (options) { var forceRefresh = !!(options && options.forceRefresh) var response = await fetch( '/your-backend/froomle-recommendations-token?refresh=' + (forceRefresh ? '1' : '0'), { credentials: 'include' } ) if (!response.ok) { throw new Error('Token endpoint failed with status ' + response.status) } var body = await response.json() return body.token }) }, { once: true }) </script> <script defer src="https://cdn.jsdelivr.net/npm/@froomle/frontend-sdk@latest/dist/froomle.global.js" data-froomle-env="your_env" data-froomle-page-visit="home" ></script> Provider callback in module and React usage: import { setConsent, setEnvironment, setPageVisit, setRecommendationsBearerProvider } from '@froomle/frontend-sdk' setEnvironment('your_env') setPageVisit('home') setConsent(2) setRecommendationsBearerProvider(async ({ forceRefresh }) => { const response = await fetch( '/your-backend/froomle-recommendations-token?refresh=' + (forceRefresh ? '1' : '0'), { credentials: 'include' } ) if (!response.ok) { throw new Error(`Token endpoint failed with status ${response.status}`) } const body = await response.json() return body.token }) React uses the same auth setters from @froomle/frontend-sdk. The React package @froomle/frontend-sdk/react provides rendering bindings, not separate auth APIs. Minimum Initialization Minimal module-based setup (programmatic or React): import { setEnvironment, setPageVisit, setConsent } from '@froomle/frontend-sdk' setEnvironment('sample_env') setPageVisit('home') setConsent(2) Minimal module + markup setup (explicit DOM mode): import { init, setEnvironment, setPageVisit, setConsent } from '@froomle/frontend-sdk' setEnvironment('sample_env') setPageVisit('home') setConsent(2) init() Minimal plain HTML setup: <script> window.addEventListener('froomle:before-init', function (event) { var sdk = event.detail && event.detail.sdk if (!sdk) return sdk.setConsent(2) }, { once: true }) </script> <script defer src="https://cdn.jsdelivr.net/npm/@froomle/frontend-sdk@latest/dist/froomle.global.js" data-froomle-env="sample_env" data-froomle-page-visit="home" ></script> Recommendations in Markup Recommendation Placeholder data-froomle-reco marks a recommendation template. Each placeholder card usually maps fields with data-froomle-param-*. How binding works: SDK requests items for each list. Each placeholder marked with data-froomle-reco receives one returned item. data-froomle-param-* attributes map item fields into DOM attributes/text. Filled nodes receive internal metadata attributes used for tracking. <article class="reco-card" data-froomle-reco="recommended_for_you"> <a data-froomle-param-href="uri" href="#"> <img data-froomle-param-src="images[0]" data-froomle-param-alt="title" alt="" /> <h3 data-froomle-param-inner="title">Loading recommendation...</h3> </a> </article> Use multiple placeholder blocks to control count (for example, 12 placeholders ⇒ up to 12 returned items). This is a slot-based model: one placeholder represents one rendered recommendation item. When multiple placeholders target the same list with compatible configuration, the SDK batches them into a backend list request automatically. Dynamic Injection and Refill The SDK observes DOM mutations after initial load when declarative DOM mode is active. This applies to: script-tag/global usage (froomle.global.js) module/import placeholder usage after explicit init() It does not apply to React bindings by default. Raw declarative data-froomle-* blocks injected onto a React page stay idle unless you explicitly start DOM placeholder mode yourself. This means: New nodes injected with data-froomle-reco can be auto-filled after page load. Existing nodes can be refilled when you clear metadata and toggle data-froomle-reco. Refill rule in current runtime: A target is eligible when both data-froomle-request-id and data-froomle-id are missing or empty. To force refill, clear those attributes, then remove/re-set data-froomle-reco. Recommendation Parameters: data-froomle-param-* Common mappings: data-froomle-param-href="uri" data-froomle-param-src="images[0]" data-froomle-param-alt="title" data-froomle-param-inner="title" Nested fields are supported, for example: <span data-froomle-param-inner="item_attributes.fq_item_id"></span> Expression mode is also supported by prefixing with =: data-froomle-param-src="=photoUrl | replace('X,','NWB,') | replace('X.','NWB.')" data-froomle-param-href="=uri | append('?from=reco')" data-froomle-param-inner="=item_attributes.category_label | defaultField(item_attributes.main_tag) | default('No category')" data-froomle-param-href="=item_attributes.category_url | defaultField(item_attributes.main_tag_link) | default('#')" For composed values (for example srcset), use template placeholders: data-froomle-param-srcset="\${photoUrl | replace('X,','NWB,')}, \${photoUrl | replace('X,','NWBR,')} 2x" Scope: This transform syntax is for declarative HTML mapping (data-froomle-param-*). It is not executed inside framework prop systems (React/Vue directives). In React, apply transforms in your component code and render final values in JSX. Supported transforms: replace(from, to) prepend(value) append(value) default(value) (used when current value is empty) defaultField(other.path) (used when current value is empty and you want to fall back to another recommendation field, for example during schema migrations or field renames) trim lower upper urlencode What is supported: Field access (including nested paths) Transform chains with the supported transform set Template interpolation with \${…} for composed attributes (srcset, combined labels, etc.) Rename-migration fallback patterns such as category_label → main_tag while old and new fields coexist What is not supported: Running custom code from attribute strings if/else, loops, or arbitrary scripting in markup Arbitrary transform arguments that execute as nested expressions Regex/backreference syntax in transforms Filters and Variables Use list-level filters and variables in markup: data-froomle-reco-filter-*: list filters (sent as arrays by SDK for HTML usage) data-froomle-variable-*: list variables <article data-froomle-reco="sample_list_name" data-froomle-reco-filter-race_types="MotoGP" data-froomle-reco-filter-article_type="News" data-froomle-variable-slot="1" > <h3 data-froomle-param-inner="title"></h3> </article> How HTML values are converted: data-froomle-reco-filter-* a single value such as MotoGP becomes ["MotoGP"] data-froomle-reco-filter-* a semicolon-separated value such as Monde;Sports becomes ["Monde", "Sports"] data-froomle-reco-filter-* a JSON array string such as ["Monde","Sports"] becomes ["Monde", "Sports"] data-froomle-variable-* a single value such as homepage stays scalar data-froomle-variable-* a semicolon-separated value becomes an array data-froomle-variable-* a JSON array string becomes an array Example request shapes: { "lists": [ { "list_name": "recommended_for_you", "categories": ["Monde"], "slot": "homepage" }, { "list_name": "recommended_for_you", "categories": ["Monde", "Sports"], "access_types": ["FREE", "PAID"] } ] } List merging behavior: Blocks with same list_name, same filters, and same list variables can be merged into one request. Add a differentiator such as data-froomle-variable-slot to keep blocks separate. Request-root variables must be set in JS/TS, not via markup: import { setCustomVariable } from '@froomle/frontend-sdk' setCustomVariable('section', '12345') Type note: setCustomVariable is typed as setCustomVariable(key: string, value: unknown). Use application-side narrowing when you read values back in your own code. Item IDs and Histories Tag content items with IDs when possible: <article class="news-card" data-froomle-id="sample_item_id_1"> ... </article> This improves analytics/event attribution and helps history handling. Inject histories (simple IDs): import { addHistories } from '@froomle/frontend-sdk' addHistories([ 'sample_item_id_1', 'sample_item_id_2' ]) addHistories([…]) generates histories.pageviews. For string IDs, item_type defaults to article. Inject typed histories when explicit item_type is needed: import { addHistories } from '@froomle/frontend-sdk' addHistories([ { id: 'sample_item_id_1', item_type: 'article' }, { id: 'sample_item_id_2', item_type: 'video' }, { id: 'sample_item_id_3', itemType: 'podcast' } // `itemType` alias is supported ], 'article') // fallback item_type for entries without explicit type Behavior note: addHistories(…) appends entries to histories.pageviews in outgoing recommendation requests. For string entries, SDK uses defaultItemType (or article when omitted). For object entries, SDK uses item_type or itemType; if missing, it falls back to defaultItemType (or article). setCustomVariable('histories', …) still writes the raw histories field directly. If both are used, the explicit custom histories value is applied. This is separate from context_item_type (used for detail context and detail_pageview), which defaults to article when omitted. Ordering Sections Use ordering when you want to reorder existing blocks from recommendation signals. Ordering Container <div data-froomle-order-categories="recommended_for_you"> ... </div> categories is the field to order by. recommended_for_you is the list used for ordering. Elements To Order Children that participate in ordering need data-froomle-ordervalue: <div data-froomle-order-categories="recommended_for_you"> <section data-froomle-ordervalue="Sports">Sports block</section> <section data-froomle-ordervalue="Politique">Politics block</section> <section data-froomle-ordervalue="Monde">World block</section> </div> Programmatic API (JS/TS) Use programmatic mode when markup placeholders are not enough. Typical cases: You want to fetch recommendations before rendering UI. You want explicit batching control across multiple requests. You render custom UI and must send events manually. No data-froomle-* placeholders are required in this mode. For setter scope, see Runtime Scope Guide. Programmatic integrations still use shared page/app-level SDK state (setEnvironment, setPageVisit, setChannel, setConsent, setDeviceId, setUserId, setContextItem, setContextItemType), while list/filter/request arguments are supplied per API call. Need a full runnable project for this mode? Use frontend-sdk-examples for programmatic JS/TS and retail-oriented examples. How it works: Configure SDK state (setEnvironment, setPageVisit, consent, identity). Request recommendation items (getRecommendations or proxy flow). Render with your own UI code. Track events explicitly with the dedicated helper functions when available, or with sendEvent(…) for custom event types. Request Models The SDK exposes two request models: API / integration shape Model Result shape getRecommendations(…), useRecoList(…) List-based One call or hook returns one or more recommendation lists, and each list can contain multiple items. proxyReco(…), useCreateReco(…), declarative data-froomle-reco placeholders Slot-based One proxy, hook call, or placeholder represents one rendered recommendation item. The SDK batches compatible slots into backend list requests automatically. Use the list-based model when you want to handle arrays of items directly in your own code. Use the slot-based model when recommendations are rendered as repeated cards/placeholders and you want the SDK to batch compatible requests automatically. getRecommendations import { getRecommendations } from '@froomle/frontend-sdk' const response = await getRecommendations([ { list_name: 'recommended_for_you', limit: 4, list_size: 4 } ]) const items = response.items() Type note: list entries are typed with required limit, list_name, and list_size, plus optional additional keys typed as [key: string]: unknown. Response item shape: response.items() returns RecommendationItem[]. Each RecommendationItem always includes: Id: string RequestId: string UserGroup: string Content fields are model/backend-dependent and are accessed via item.get(key). There is no single fixed payload schema for these dynamic keys. for (const item of response.items()) { const id = item.Id const requestId = item.RequestId const userGroup = item.UserGroup // Dynamic payload fields depend on your recommendation setup. const title = item.get('title') const uri = item.get('uri') const image = item.get('image') ?? item.get('images')?.[0] } Error handling: getRecommendations(…) returns a promise and rejects on request/HTTP/response-parse failures. Handle with try/catch (or .catch(…)) in programmatic flows. try { const response = await getRecommendations([ { list_name: 'recommended_for_you', limit: 4, list_size: 4 } ]) // use response } catch (error) { // network / HTTP / parse failure } Need full request payload rules (required fields, list structure, histories, batching)? See Recommendation requests. proxyReco + fulfillRecommendations Queue multiple slot requests first, then resolve them in one batch call. This is the same slot-based model used by declarative markup and React helpers: one proxyReco(…) call represents one rendered item not one returned list compatible queued proxies are merged into one backend request when fulfillRecommendations() runs import { proxyReco, fulfillRecommendations } from '@froomle/frontend-sdk' const first = proxyReco({ list: 'recommended_for_you', filters: [] }) const second = proxyReco({ list: 'recommended_for_you', filters: [] }) await fulfillRecommendations() const [itemA, itemB] = await Promise.all([first, second]) Error handling: proxyReco(…) returns a promise-like recommendation handle; it does not resolve to null. Each proxy resolves to a RecommendationItem or rejects. Rejection cases include: batch request failure in fulfillRecommendations() fewer returned items than queued requests (No recommendation available) const first = proxyReco({ list: 'recommended_for_you', filters: [] }) const second = proxyReco({ list: 'recommended_for_you', filters: [] }) try { await fulfillRecommendations() } catch (error) { // batch request failed; queued proxies reject } const results = await Promise.allSettled([first, second]) Manual Event Helpers Use the dedicated helper functions for the common manual/programmatic flows the SDK now models explicitly. import { sendAddToCart, sendItemInteraction, sendPurchase, sendRemoveFromCart, sendUserInteraction } from '@froomle/frontend-sdk' // Uses current context_item / context_item_type from SDK state sendItemInteraction('product_like') sendUserInteraction('submit_review') sendAddToCart(2, { basket_content: [{ id: 'product-111' }, { id: 'product-987' }] }) sendRemoveFromCart(1, { basket_content: [{ id: 'product-111' }] }) sendPurchase(2, 19.99, { original_price: 29.99 }) These helper functions use the same runtime defaults as sendEvent(…): page_type, channel, device_id, user_id, and user_group are read from SDK state when available. For item-based helpers (sendItemInteraction, sendAddToCart, sendRemoveFromCart, sendPurchase), omit actionItem / actionItemType to reuse the current context_item / context_item_type. If you pass explicit actionItem / actionItemType, those values win over context. If an item-based helper has neither explicit item args nor current context, it does not emit an event. Use these helpers for item_interaction, user_interaction, add_to_cart, remove_from_cart, and purchase. Keep sendEvent(…) for unsupported/custom event types or when you need total payload control. Retail Trigger-Based Events For retail flows, wire the dedicated helper functions directly to the business trigger in your UI code. Do not rely on generic click tracking for these events. Typical examples: sendAddToCart(…) from an Add to Cart button or cart form submit. sendRemoveFromCart(…) from a remove-line action in the basket. sendPurchase(…) from the confirmed checkout or thank-you step, not from the initial buy button. sendItemInteraction(…) for actions such as wishlist, favorite, or compare. sendUserInteraction(…) for non-item flows such as review submit, quote request, or starting a chat. import { sendAddToCart, sendItemInteraction, sendPurchase, sendRemoveFromCart, sendUserInteraction, setContextItem, setContextItemType, setPageVisit, setChannel } from '@froomle/frontend-sdk' setPageVisit('product_detail') setChannel('www-desktop') setContextItem('32636759') setContextItemType('product') buttonAddToCart.onclick = () => { sendAddToCart(2, { basket_content: [{ id: '32636759' }], merchandising_slot: 'product-detail' }) } buttonWishlist.onclick = () => { sendItemInteraction('product_like', { source: 'product-detail' }) } buttonReview.onclick = () => { sendUserInteraction('submit_review', { review_surface: 'product-detail' }) } buttonRemoveFromCart.onclick = () => { sendRemoveFromCart('32636759', 'product', 1, { basket_content: [] }) } confirmPurchase.onclick = () => { sendPurchase(1, 119.99, { original_price: 149.99, checkout_id: 'checkout-2048' }) } Retail helper guidance: Prefer the dedicated helper matching the business action instead of raw sendEvent(…). Set context_item / context_item_type on product-detail pages when you want the short helper forms. Use explicit item args when the triggered item differs from the current page context, such as a basket line or variant selector. extras is merged last, so add flow-specific fields such as basket_content, original_price, checkout_id, or merchandising_slot there. For recommendation-related attribution fields (list_name, request_id, user_group), keep using sendEvent(…) or pass those fields explicitly when your custom flow requires them. sendEvent Use sendEvent(…) when you need explicit control over an event type or payload that is not covered by the dedicated helper functions. import { sendEvent } from '@froomle/frontend-sdk' sendEvent('sample_item_id_1', 'article', 'impression', { list_name: 'recommended_for_you', request_id: 'req-id' }) Need the full event contract (event types, required fields, and delivery guidance)? See Tracking events. You normally should not need sendEvent(…) for standard e-commerce triggers. For first-party support of retail flows, use the dedicated helpers from Retail Trigger-Based Events instead: sendItemInteraction(…), sendUserInteraction(…), sendAddToCart(…), sendRemoveFromCart(…), and sendPurchase(…). sendEvent auto-populates base fields from SDK runtime state: event_type, page_type, channel, device_id, and (when available) user_id and user_group. action_item / action_item_type come from the call, but if the runtime receives undefined values in JS it falls back to context_item / context_item_type when present. extras is merged last. Use it to add fields such as list_name or request_id, or to intentionally override default fields (for example page_type / channel) in custom flows. Script-tag attributes are bootstrap inputs for SDK state; they are not per-event arguments to sendEvent(…). For programmatic integrations, event payloads and event types must follow Tracking events. extras requirement guide: Field in extras Requirement list_name, request_id For recommendation-related events (impression, click_on_recommendation), treat these as required for reliable attribution/reporting. For non-recommendation events, they are optional. user_group Required for recommendation-related events. The SDK auto-populates it when available from runtime/DOM state; pass it explicitly in custom flows when that state is unavailable. user_id Usually inferred automatically from SDK state when consent allows and a user_id has been set. Pass it explicitly only when you intentionally want to override the current SDK user context. page_type, channel, device_id Optional overrides only. The SDK already sets these from runtime state. React Bindings React helpers are exposed from @froomle/frontend-sdk/react. Use this layer when your recommendation blocks are React components instead of DOM templates with data-froomle-*. For setter scope, see Runtime Scope Guide. React integrations still use shared page/app-level SDK state (setEnvironment, setPageVisit, setChannel, setConsent, setDeviceId, setUserId, setContextItem, setContextItemType), while each recommendation block/component supplies its own list/filter/rendering configuration. Need a complete React example app instead of API snippets? Use frontend-sdk-examples. The examples repo includes React news, retail, and ArcXP examples. React bindings are intended for the client-rendered / rehydrated React layer of your app. Use this pattern in a browser entry such as main.jsx, or in a framework-specific client bootstrap that runs under the real React app root. Do not mount FroomleSdkInit from a server-only shell, output type, or SSR template wrapper that never rehydrates on the client. React bindings do not call init() and do not start declarative DOM placeholder mode. Raw declarative data-froomle-* blocks injected onto a React page stay idle unless you explicitly start DOM placeholder mode yourself. Mixing those modes on the same page is not the preferred integration path. In hybrid frameworks, browser-persistent setters such as setConsent(…), setUserId(…), and setDeviceId(…) must run in browser context unless your host platform sets equivalent cookies itself on the server response. If you only need shell-level integration and automatic SDK-managed events (page_visit, detail_pageview, impression, click_on_recommendation), prefer Script-Tag Setup and the runnable plain HTML examples in frontend-sdk-examples instead of forcing the React bindings into the server shell. ArcXP / Fusion In ArcXP / Fusion, use the following runtime model: the Output Type is the server-rendered shell the client-rendered / rehydrated React layer runs under #fusion-app Recommended ArcXP integration: Initialize page/app-level SDK state in the client-rendered React layer. Mount FroomleSdkInit there. Render recommendation UI with useCreateReco or useRecoList, together with FroomleReco / useReco, there. Keep the Output Type limited to server-shell responsibilities. The following setters define page/app-level runtime state and are normally set once per page context, not once per recommendation block: setEnvironment(…) setPageVisit(…) setChannel(…) setConsent(…) setDeviceId(…) setUserId(…) setContextItem(…) setContextItemType(…) If a page contains multiple recommendation blocks, set these values once for the current page context and render the blocks against that shared SDK state. If the page context changes during client-side navigation, update the page-level setters once for the new route/context. ArcXP integration options: Path Use when Notes React bindings in the client-rendered layer under #fusion-app Recommendations are part of the React app itself. Preferred ArcXP integration. Use @froomle/frontend-sdk/react, mount FroomleSdkInit in the browser layer, and render one or more recommendation blocks from shared page-level SDK state. Script-tag/static shell Recommendation markup remains server-rendered and is not rehydrated on the client. Narrow exception only. Use this in the Output Type shell or in a feature explicitly marked .static = true. Do not use this for normal rehydrated ArcXP feature trees. Use frontend-sdk-examples for runnable examples. The examples repo includes an ArcXP React example (arcxp-native-react-example) and a static-shell exception example (arcxp-native-static-example). Mental model: Configure SDK once in the client entry (main.jsx in this example). Wrap the app with FroomleSdkInit. Choose one React rendering paradigm for each recommendation flow. Render each recommendation item through FroomleReco when you want automatic recommendation-item event tracking. Add advanced composition with FroomleOrder and FroomleCustomItem. import React from 'react' import ReactDom from 'react-dom/client' import { setEnvironment, setPageVisit, setConsent } from '@froomle/frontend-sdk' import { FroomleSdkInit } from '@froomle/frontend-sdk/react' import App from './App' setEnvironment('sample_env') setPageVisit('home') setConsent(2) ReactDom.createRoot(document.getElementById('root')).render( <FroomleSdkInit> <App /> </FroomleSdkInit> ) Core React primitives: useCreateReco({ list, filters }) useRecoList({ list, filters, limit, list_size }) FroomleReco useReco FroomleOrder and FroomleOrderItem FroomleCustomItem Supported React Paradigms Paradigm Use when Behavior Slot-based rendering with useCreateReco(…) One rendered card or slot maps to one recommendation item. One hook call returns one recommendation handle. Compatible hook calls are batched into backend list requests automatically. List-based rendering with useRecoList(…) A parent component owns one recommendation request, transforms the returned list once, and then passes data to presentational children. One hook call returns { status, loading, error, response, list, items }. Returned items can still be rendered inside FroomleReco to preserve automatic recommendation-item tracking. React supports both paradigms, but they are not interchangeable: useCreateReco(…) is slot-based. One hook call returns one recommendation item, not a list of items. useRecoList(…) is list-based. It returns all items for one list request, but automatic impression and click_on_recommendation tracking still depends on rendering each returned item through FroomleReco. If you render raw markup from useRecoList(…) without FroomleReco, treat impression and click_on_recommendation as a manual responsibility. React Component Behavior useCreateReco useCreateReco(…) returns a recommendation handle, not just a settled item. Return value: Type: RecommendationItem & PromiseLike<RecommendationItem>. You pass this handle to <FroomleReco reco={…}>. The same handle also exposes recommendation fields (for example reco.uri) once resolved. Dynamic keys can always be read with reco.get('field_name'). Practical guidance: One useCreateReco(…) call represents one recommendation item, not a list of items. If you want 4 recommendation cards, render 4 hook calls / 4 FroomleReco wrappers. Compatible useCreateReco(…) calls are batched into one backend request automatically. If you want the clearest render flow, read recommendation fields via useReco() inside a FroomleReco subtree. Direct field access on the handle is supported, but those fields are empty until the reco resolves. useRecoList useRecoList(…) returns a resolved recommendation list for parent-owned rendering flows. Return value: Type: { status, loading, error, response, list, items }. items is RecommendationItem[]. list is the resolved list object (or null while loading / on error). response is the full response object (or null while loading / on error). Practical guidance: Use this when a parent component wants one list request and then maps the returned items into presentational children. limit and list_size remain caller-configurable, for example from CMS settings. Returned items can still be rendered inside FroomleReco so automatic impression and click_on_recommendation tracking continues to work. useRecoList(…) is list-based. It does not rely on the slot-batching behavior of useCreateReco(…). The React hook returns runtime objects. You do not import generated RecommendationList or Recommendations constructors from the root SDK package. FroomleReco FroomleReco is a wrapper component around a single recommendation handle. Props: reco: recommendation handle from useCreateReco(…), or a resolved RecommendationItem from useRecoList(…). children: render content. Any extra props (id, className, data-*, …) are forwarded to its wrapper element. Runtime behavior: Renders a <div> wrapper and places children inside it. Does not clone or replace children. Resolves reco and writes recommendation metadata on the wrapper: data-froomle-reco (when list is known), data-froomle-id, data-froomle-item-type, data-froomle-request-id, data-froomle-user-group. Provides the reco value through React context for useReco(). useReco() behavior: Returns the current recommendation from the nearest FroomleReco provider. Throws if called outside a FroomleReco subtree. FroomleCustomItem FroomleCustomItem is for manually inserted editorial/custom items that should still influence recommendation history. Props: id: item id to add to histories. children: rendered content. Runtime behavior: Renders children as-is in a fragment (no extra DOM wrapper). Calls addHistories([id]) once per mount. Does not fetch recommendations. Does not inject recommendation attributes or clone children. Example usage: import { FroomleReco, FroomleOrder, FroomleOrderItem, FroomleCustomItem, useCreateReco, useReco } from '@froomle/frontend-sdk/react' function RecoLink() { const reco = useReco() return <a href={reco.get('uri')}>{reco.get('title')}</a> } function Example() { const recoHandle = useCreateReco({ list: 'recommended_for_you', filters: [] }) return ( <> <FroomleReco reco={recoHandle}> <RecoLink /> </FroomleReco> <FroomleOrder list="recommended_for_you" category="categories"> <FroomleOrderItem value="Sports"><section>Sports</section></FroomleOrderItem> <FroomleOrderItem value="Monde"><section>World</section></FroomleOrderItem> </FroomleOrder> <FroomleCustomItem id="sample_item_id_1"> <article>Editorial item</article> </FroomleCustomItem> </> ) } Advanced Integration Notes Script-tag Whitelist Only these attributes are read from the SDK <script> tag: data-froomle-env data-froomle-page-visit data-froomle-context-item data-froomle-context-item-type data-froomle-request-domain data-froomle-device-id data-froomle-user-id data-froomle-safe-request data-froomle-channel Everything else should be set via JS/TS setters. Event Tracking Behavior The SDK handles common tracking automatically for DOM-based recommendation rendering. For custom flows and programmatic rendering, use the manual event helpers for supported flows, or sendEvent(…) when you need the low-level fallback. If you send events manually, adhere to the standard event contract documented in Tracking events. Practical rule: If you render recommendations yourself, send impression/click events yourself. Include list_name and request_id for reliable reporting attribution. action_item_type resolution: detail_pageview: uses context_item_type, defaults to article. impression and click_on_recommendation: use data-froomle-item-type (or data-froomle-action-item-type) when present, otherwise default to article. DOM-filled recommendation nodes automatically get data-froomle-item-type when item_type exists in the recommendation response. Troubleshooting and Validation When recommendations do not appear: Verify setEnvironment / data-froomle-env. Verify setPageVisit / data-froomle-page-visit. Check list name (data-froomle-reco or programmatic request list). Check field mappings (uri, images[0], title, or your schema fields). Check network requests to /recommendations/requests. For consent tests, run on http://localhost (cookies do not behave reliably on file://). In webpack dev server setups, keep HMR/live reload disabled when debugging SDK DOM behavior. Stack-specific checks: Plain HTML: confirm SDK script loads (no CSP/CORS blocking in console). TS/Webpack: confirm SDK init code runs before placeholders are rendered. JSP + TS: confirm the compiled JS bundle is generated and loaded by the JSP page. JSP + TS: for server 503 responses, inspect application server logs and verify JSP/web configuration. Programmatic: if UI renders but events are missing, verify SDK state is initialized (setEnvironment, setPageVisit, consent), and verify helper arguments or sendEvent(…) extras include the required event fields for your flow. React: verify app is wrapped in FroomleSdkInit and list/filter args passed to useCreateReco are correct. For full end-to-end sample projects, see frontend-sdk-examples.