Widgets Documentation

Overview

Live personalization widgets — popups, banners, countdowns, and toasts

Adverfly Widgets render personalized popups, banners, countdowns and toast notifications on your storefront. The right variant is picked per visitor in real time by the personalization engine based on the segment they belong to.

How it works

Visitor lands on page
   ↓
Pixel calls /public/personalize with customer_id + session context
   ↓
Engine evaluates active widgets for this trigger + visitor's segment
   ↓
Pixel renders the winning widget (popup / banner / countdown / toast)
   ↓
All interactions (impression, click, dismiss, success) tracked to ClickHouse

You design widgets in the Adverfly app under Widgets. Each widget targets one segment (or "Everyone") and is auto-rendered when its trigger fires.

Installation

The widget pixel ships as part of the standard Adverfly tracking pixel — if you already have preset-pixel-adv.js (v3) installed, widgets work automatically with no extra code.

Shopify

Paste the Adverfly script tag into your theme's layout/theme.liquid (just before </head>) — same pattern as Google Analytics or any other tag. The pixel ships the widget renderer in the storefront DOM, where the Shopify Web Pixel sandbox can't reach.

<script async src="https://cdn.adverfly.com/preset-pixel-adv.js"></script>
<script>
  window.adverfly = window.adverfly || [];
  window.adverfly.activate_widgets = true;
  advPxl("init", {{ shop.metafields.adverfly.workspace_id | default: 'YOUR_WORKSPACE_ID' }});
</script>

The pixel auto-detects Shopify and skips auto-firing pageview / vikey events to avoid double-counting — those are tracked by the Shopify Web Pixel install. Widgets render normally.

Inline placements: drop a <div data-adv-inline data-adv-trigger="…" data-adv-workspace="…"> into any section file (see Inline Widgets below).

Other platforms (theme.liquid, GTM, manual)

If you already have the Adverfly pixel on your site, enable widgets per workspace by setting window.adverfly.activate_widgets = true before advPxl("init", ...). Without this flag, the pixel does not call /personalize automatically — manual advWidgets.fetchAndRender(...) calls still work.

Request
window.adverfly = window.adverfly || [];
window.adverfly.activate_widgets = true;
advPxl("init", 8397799);

Auto-Triggers

Once activate_widgets = true is set, widgets fire automatically based on their trigger_type:

CodeDescription
page_viewOn every page load.
exit_intentWhen the cursor leaves the top of the page.
post_purchaseAfter a `conversion` event fires (set window.adverfly.transaction_id first).
scroll_depthAfter visitor scrolls past N% of the page.
time_on_pageAfter N seconds on the page.

Manual Rendering

If you want to trigger a widget yourself (e.g. after a custom event in your app), call advWidgets.fetchAndRender:

Request
/* Trigger a custom widget configured for trigger="custom_event" */
window.advWidgets.fetchAndRender("custom_event");

/* Pass extra context that's added to the engine evaluation */
window.advWidgets.fetchAndRender("post_purchase", {
  transaction_id: "order_123",
  cart_value: 49.9,
});

Inline Widgets

Inline widgets render at a specific spot in your page rather than as overlays. Drop a <div> with data-adv-inline into your theme:

Request
<div
  data-adv-inline
  data-adv-trigger="pdp_above_cta"
  data-adv-workspace="WORKSPACE_ID"
  style="min-height: 80px"
></div>

Configure a widget in Adverfly with trigger_type = "inline" and a matching trigger value (e.g. pdp_above_cta). The pixel mounts the personalized variant inside that div on page load.

ParameterTypeRequiredDescription
data-adv-inlineflagRequiredMarks the element as an inline widget mount.
data-adv-triggerstringRequiredTrigger name to fetch from Adverfly. Unique per placement.
data-adv-workspacestringRequiredYour Adverfly workspace ID.

Tracked Events

Every widget interaction is logged to ClickHouse pixel_events automatically — same pipeline as survey events, joinable by customer_id.

CodeDescription
widget_impressionWidget rendered into the DOM.
widget_clickUser clicked the CTA. Properties include cta_url.
widget_dismissUser closed the widget manually (X button).
widget_auto_dismissedToast timer expired or countdown ran out.
widget_successGoal completed — fired manually via window.advWidgets.success(...).

Common payload fields

Every widget event carries these top-level fields (matches the survey schema for easy joining):

ParameterTypeRequiredDescription
form_idstringRequiredThe widget's variant_id (column reused for queryability).
customer_idstringOptionalSet via window.adverfly.customer_id — automatically hashed server-side.
transaction_idstringOptionalOrder ID if available — enables conversion attribution.
propertiesJSONRequiredStringified JSON with widget_type, trigger, and event-specific fields like cta_url.

Tracking Goal Completion (Success API)

When a visitor completes the goal of a widget — submits an email, claims a discount, signs up — call the success API. This fires a widget_success event you can query later for conversion analytics.

Request
window.advWidgets.success("widget-variant-id", {
  email: "user@example.com",   // auto-hashed → email_hashed (SHA-256, lowercased)
  value: 19.9,
  source: "newsletter_form",
});

The raw email is never transmitted — the pixel hashes it client-side before sending. Pass email_hashed directly if you've already hashed elsewhere.

ParameterTypeRequiredDescription
variantIdstringRequiredThe widget's variant_id (visible in Adverfly under the widget detail).
props.emailstringOptionalAuto-hashed to email_hashed. Raw value never leaves the browser.
props.email_hashedstringOptionalSkip hashing — pass an already-hashed value (SHA-256 of lowercased email).
props.valuenumberOptionalOptional value associated with the success (e.g. discount amount, order value).
props.*anyOptionalAny custom properties — included verbatim in the event payload.

Manual Click Tracking

For inline widgets that render custom HTML, attribute clicks manually:

Request
window.advWidgets.trackClick("widget-variant-id", {
  cta_url: "/checkout",
  source: "custom_cta",
});

Joining Widgets to Conversions

Widget events live in pixel_events and conversions in pixel_conversions. Join by customer_id (hashed) or by visitor_id for anonymous attribution.

Request
-- Click-through rate per widget, last 30 days
SELECT
  form_id AS widget_id,
  countIf(name = 'widget_impression')      AS impressions,
  countIf(name = 'widget_click')           AS clicks,
  countIf(name = 'widget_success')         AS successes,
  clicks / nullIf(impressions, 0)          AS ctr
FROM pixel_events
WHERE store_id = {workspace_id:String}
  AND name LIKE 'widget_%'
  AND dt >= today() - 30
GROUP BY form_id;

-- Attribute conversions to a widget impression (24h window, visitor_id link)
SELECT
  e.form_id AS widget_id,
  sum(c.transaction_gross_revenue / 100) AS attributed_revenue
FROM pixel_events AS e
INNER JOIN pixel_conversions AS c
  ON e.visitor_id = c.visitor_id
 AND e.store_id   = c.store_id
 AND c.dt BETWEEN e.dt AND e.dt + INTERVAL 1 DAY
WHERE e.store_id = {workspace_id:String}
  AND e.name = 'widget_impression'
GROUP BY e.form_id;

Manual-Only Mode

If activate_widgets = true is not set, no auto-triggers fire — but window.advWidgets.fetchAndRender(trigger, extra) still works for fully manual rendering. Use this if you want full control over when /personalize is called.

See Also

  • Tracking Events — base event API the widget events ride on
  • Surveys — sister product, same event pipeline, similar embed pattern
  • Pixel Installation — how the pixel boots and the window.adverfly queue