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 Build recommendation blocks with markup: Recommendations in Markup Fetch and render recommendations in code: Programmatic API (JS/TS) Integrate in React: React Bindings Debug setup and runtime issues: Troubleshooting and Validation 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, send events manually with sendEvent(…). Programmatic (JS/TS API) No Send events manually with sendEvent(…) and 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). 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. 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, …). 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. 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, or runtime identity. 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. 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 in code: import { setEnvironment, setPageVisit, setConsent } from '@froomle/frontend-sdk' setEnvironment('your_env') setPageVisit('home') setConsent(2) 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 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). 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 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 setContextItem setContextItemType setCustomVariable addHistories getRecommendations proxyReco fulfillRecommendations sendEvent Code-first integration. Use this for explicit runtime control, programmatic fetching, and manual event flows. 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?) Send explicit tracking events for custom/programmatic rendering flows. 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, fulfillRecommendations, getRecommendations, proxyReco, sendEvent, setConsent, 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 } Current signature highlights from the published declarations: setConsent(consent: number): void setUserId(userId: string): void setDeviceId(deviceId: string): void setCustomVariable(key: string, value: unknown): void getRecommendations(lists: Array<{ limit: number list_name: string list_size: number [key: string]: unknown }>): Promise<Recommendations> 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') Responsive channel example: import { setChannel } from '@froomle/frontend-sdk' const channel = window.matchMedia('(max-width: 767px)').matches ? 'www-mobile' : 'www-desktop' setChannel(channel) Minimum Initialization Minimal module-based setup: import { setEnvironment, setPageVisit, setConsent } from '@froomle/frontend-sdk' setEnvironment('sample_env') setPageVisit('home') setConsent(2) 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). Dynamic Injection and Refill The SDK observes DOM mutations after initial load. 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')" 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) 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.) What is not supported: Running custom code from attribute strings if/else, loops, or arbitrary scripting in markup 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> 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. 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 sendEvent when required by your reporting setup. 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 requests first, then resolve in one batch call. 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]) sendEvent Use manual event tracking when you need explicit control. import { sendEvent } from '@froomle/frontend-sdk' sendEvent('sample_item_id_1', 'article', 'impression', { list_name: 'recommended_for_you', request_id: 'req-id', user_id: 'user-456' }) Need the full event contract (event types, required fields, and delivery guidance)? See Tracking events. sendEvent auto-populates base fields from SDK runtime state: event_type, action_item, action_item_type, page_type, channel, device_id, and (when available) user_group. extras is merged last. Use it to add fields such as list_name, request_id, user_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 Optional per call. Include when the user is identified and consent allows tracking. sendEvent(…) does not auto-add user_id. 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-*. Mental model: Configure SDK once in main.jsx. Wrap the app with FroomleSdkInit. Create recommendation handles with useCreateReco. Render with FroomleReco / useReco. 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 }) FroomleReco useReco FroomleOrder and FroomleOrderItem FroomleCustomItem 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: 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. FroomleReco FroomleReco is a wrapper component around a single recommendation handle. Props: reco: recommendation handle from useCreateReco(…) (or equivalent). 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 sendEvent(…) explicitly. 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 sendEvent(…) extras include required list metadata (list_name, request_id) and any custom overrides. 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.