How the Widget Renders (and Why It Sometimes Hides)
On page load, the recommendation widget requests data from Piano. In most implementations you'll see an XHR/Fetch request similar to:
/public/widget/data?...moduleId=
Common behaviors:
-
If the JSON payload returns an empty array (for example,
"items":[]), the module typically suppresses itself (often by applyingdisplay:none). -
If the request fails (
40x/50x), the module also won't render. -
If required scripts don't load or are blocked, the widget container/iframe may remain empty.
Check the Widget Data Request in the Browser
-
Open the affected page.
-
Open Developer Tools → Network.
-
Filter by Fetch/XHR and reload.
-
Find the request ending in
/public/widget/data.
Interpret what you see:
|
What You See |
What It Usually Means |
Next Step |
|---|---|---|
|
|
No eligible content returned (indexing, filters, targeting variables, consent) |
Continue to this section |
|
|
Request is failing (integration, script, environment, or blocking) |
Continue to this section |
|
No |
Widget never initialized or scripts/experience didn't execute |
Continue to this and this section |
Verify Content Supply
If "items":[], first confirm the underlying content exists and is indexed.
Confirm the Article Is Indexed
Use the document search endpoint to check a specific URL:
/document/search?url=
If you get 0 results, the crawler likely did not index the page correctly (or indexed the wrong content).
Common Indexing Blockers
-
CMP/cookie banner blocking the crawler: The crawler may fetch the consent layer instead of the article body. Whitelist the Piano crawler in your CMP and any relevant firewall policies.
-
Missing or incomplete publish time metadata: Ensure is present and includes a complete time value (date plus hour/minute).
-
Canonical/URL conflicts or duplicates: Missing or inconsistent canonical information can cause duplicate filtering or mismatches. Ensure canonical URL signals are consistent (for example, ,
og:url, andcXenseParse:urlif used). -
Domain not live or not properly configured: If the domain isn't live or correctly configured in Piano, content may not be available for modules.
Required/Commonly Used Metadata
Ensure these are accurate and stable across templates:
<meta name="cXenseParse:url" content="https://www.example.com/path/to/article"/>
<link rel="canonical" href="https://www.example.com/path/to/article"/>
<meta property="article:published_time" content="2026-02-12T08:30:00+00:00"/>
After fixing metadata or CMP blocking, republish affected articles or request a recrawl. Some changes appear quickly, but broader crawling updates can take longer (up to approximately a day in some cases).
Confirm the Core Recommendation Scripts Load
Recommendations are delivered via cx.cce.js (which loads/uses cx.js). If those scripts don't load, widgets can't render.
What to Check
-
In the Network tab, confirm
cx.cce.jsandcx.jsare requested and return200. -
Look for blocked requests (for example,
(blocked)ornet::ERR_BLOCKED_BY_CLIENT), which often indicates an ad blocker or CMP policy.
Ensure Site ID Is Set
Your Piano snippet should include the Site ID call before recommendations execute:
tp.push(["setCxenseSiteId", "YOUR_SITE_ID"]);
Testing Script Loading Directly
In some debugging scenarios you can explicitly include the script:
<script type="text/javascript" async="async" src="https://cdn.cxense.com/cx.cce.js"></script>
If explicitly including it "fixes" the issue, investigate script loading order or conflicts that prevented Piano from loading it automatically.
Validate Consent Behavior
Consent can block recommendations in two ways:
-
The CMP can block loading of recommendation scripts/domains.
-
Consent state can suppress recommendation delivery.
Key points:
-
If the
recsconsent class is rejected, personalized recommendations are suppressed. -
If the
pvconsent class is rejected, it can implicitly reject other classes (includingrecs) and prevent recommendations from loading.
To verify consent state, run the following in the browser console:
tp.consent.getState()
Confirm pv and recs are accepted for the sessions where the widget fails (including after login, if applicable).
Check Widget/Collection Configuration
If the widget request succeeds but returns no items, validate the content source and filters:
-
Confirm the widget is linked to a non-empty collection.
-
Confirm a template is assigned to the widget (where applicable).
-
Temporarily relax or remove filters to test:
-
Max publish age (commonly too strict)
-
Segment/visitor filters
-
Any required field constraints (for example, image-required logic, tags, etc.)
-
-
If using slot/override logic, ensure at least one slot points to the intended non-override/normal collection.
Confirm your site actually has enough eligible published content within the configured age window (often "last ~90 days" is used as a quick sanity check).
Ensure Targeting Variables Are Available Before Piano Initializes
Inconsistent display can be caused by timing/race conditions: variables used for targeting are not defined when Piano evaluates the experience/widget.
Typical symptoms:
-
Works on refresh but not on first load
-
Works for some users but not others
-
"items":[]intermittently
What to do:
-
Ensure variables (for example, logged-in flags or other custom targeting values) are defined synchronously before the Piano bootstrap runs.
-
Place required meta tags and
tp.push(...)calls in the HTML (not after an AJAX call).
Diagnose Composer Execution
If there's no widget data request — or the widget is present in HTML but never executes — verify the experience is actually evaluated and allowed to run.
Use Composer Debug Mode
Append to the URL:
?xpdebug=true
Then:
-
Look for experience evaluation/execution logs.
-
Check network calls (for example, experience execution responses) to see whether targeting rules excluded the visitor/page.
Action Exclusivity Manager
If you use Action Exclusivity, confirm it isn't preventing the widget action from running. Adjust exclusivity rules or priority to allow the recommendation action to execute.
Check for JavaScript Errors and Template Robustness
Console errors can prevent rendering even when data is available.
Common causes:
-
Custom rendering template errors (for example, "Error executing custom rendering template code"):
-
Example: calling
.trim()on a value that is sometimes not a string. -
Fix: add type/exists checks before using fields.
-
-
Third-party library conflicts:
-
For example, some sites have seen conflicts where a third-party script interfered with
cx.js. -
Isolation test: temporarily disable recent/optional scripts (or load them only where needed) and re-test.
-
Special Cases
10.1 Signed-In Users Only
If recommendations fail only after login:
-
Re-check consent after authentication (ensure
pvandrecsremain accepted). -
Check for post-login JavaScript errors or scripts that only load for authenticated sessions.
-
Confirm targeting variables specific to logged-in users exist before Piano initializes.
Empty Iframes
If you see recommendation iframes but they're empty:
-
Confirm
cx.cce.jsis not blocked by the browser/CMP/ad blocker. -
Check for console exceptions related to widget rendering/template execution.
-
If many widgets load at once, test reducing concurrency and consider lazy-loading.
Missing DOM Targets
If using multi-widget rendering (for example, showPianoMultiRecos), ensure all targetElementIds exist in the DOM at the time the function runs—missing target elements will result in no visible widget.
After You Apply Fixes
-
Re-test the page and confirm
/public/widget/datareturns a non-empty"items"array. -
Confirm
cx.cce.js/cx.jsload successfully. -
If indexing/crawler-related changes were made, republish a representative article or request a recrawl and re-check
/document/search.
If problems persist, capture the following and contact Piano Support:
-
The
/public/widget/datarequest/response (status + payload) -
Console errors
-
Consent state from
tp.consent.getState() -
A sample affected article URL and its
/document/search?url=...result