Skip to content

Analytics & GTM ​

iConfigure records configurator sessions in the Manager dashboard when tracking is enabled on a product. On the host page (script inject), the tracking store also pushes events to window.dataLayer when that array exists — Google Tag Manager listens to those pushes.

Implementation source: packages/stores/tracking.js in the v2 monorepo.

Enable tracking ​

  1. Open Manager → your product → Submission & tracking.
  2. Enable Enable tracking to get insights (tracking_enabled on the product).
  3. Save and publish the product.

On load, _StartProductTracking in the form store starts a session and inserts a row in the Supabase tracking table. The same data appears on Dashboard and under Data → Export (dataset Tracking).

If the insert fails, tracking is disabled for that session (no further ic_update / ic_finish events; ic_step and ic_form_show still fire).

Tracking does not start when the configurator is opened with mode=debug or mode=manager in the URL.

iFrame vs inject ​

Embed modeHost-page GTM
iFrameConfigurator JS runs in the iframe's own window. Host-page GTM does not see dataLayer.push calls.
Script injectConfigurator runs on the host window — use this for GTM / GA4 on your site.

The code does not check embed mode explicitly; it only checks typeof window.dataLayer !== 'undefined'. Inject works because configurator and GTM share the same window.

Prerequisites on the host page ​

  1. Tracking enabled on the product in Manager.
  2. window.dataLayer must exist before the session starts — normally created by the GTM container snippet. Minimal alternative:
html
<script>window.dataLayer = window.dataLayer || [];</script>
  1. Load GTM before inject.iife.js and injectApp(). See Script inject.

GTM itself is optional; any script that defines dataLayer is enough for the pushes to land somewhere you can read.

dataLayer events (verified) ​

EventWhenSupabase
ic_setupSession starts after product loadInsert
ic_stepOn every configurator step changeUpdate
ic_form_showThe submission (quote) form becomes visible—
ic_updateFeature change recorded + debounce (see below)Update
ic_finishSuccessful submit (info step / quotation)Update

Additionally, every successful submit pushes the legacy offerte_submit event (v1 format, flat offer fields) — independent of tracking_enabled. In the configurator's submission form it fires the moment the submit button is pressed; in the cart/showroom flow and the standalone contact form it fires as soon as the quote has been saved. In the configurator and cart/showroom flows the host page only receives the quotation/finished postMessage at least 1 second after the event, so the redirect to the thank-you page never outraces it.

The ic_step and ic_form_show events use a flat shape (no data wrapper) so they are directly usable in GTM:

javascript
{ event: 'ic_step', step: <1-based number>, step_name: '<step name>', product_id, company_id, dealer_id, total_price }
{ event: 'ic_form_show', step: <1-based number>, product_id, company_id, dealer_id, total_price }

step is always the human-facing 1-based step number (UI step 2 → step: 2). The submission form is not a step value: the moment the form becomes visible is tracked by the separate ic_form_show event — fired on the output-button click that reveals the form (e.g. "vraag offerte aan"), or when the form is shown directly: on entering the info_form step when the product has no output buttons, or when every output button asks for contact details (the button screen is skipped). It fires on every reveal, so deduplicate in GTM if you need uniques.

ic_step and ic_form_show are always pushed (even without tracking_enabled); the matching updates[] write to Supabase only happens during an active tracking session for ic_step, and never for ic_form_show (dataLayer-only). The product_id / company_id / dealer_id fields are only populated during an active tracking session — without tracking_enabled they are null.

Each push has the shape:

javascript
{ event: 'ic_setup' | 'ic_update' | 'ic_finish', data: { /* tracking object */ } }

data object (all events) ​

FieldDescription
user.devicee.g. smartphone, desktop
user.browsere.g. Chrome, Mobile Safari
user.ose.g. iOS, Windows
product_idProduct UUID
company_idCompany UUID
dealer_idDealer UUID or null
emailEmpty until submit; the visitor's email on ic_finish
finishedfalse until submit; true on ic_finish
time.loadingMs from navigation start (approx.)
time.totalMs from session start (set on update/finish)
updates[]Step, feature ID, value, timestamp, timeSpentOnFeature, totalPrice

On ic_finish the data object additionally contains phone, first_name and last_name when the visitor filled them in (dataLayer only, not stored in the Manager).

ic_setup ​

Fired once in trackingSetup, immediately before the Supabase insert.

ic_update — debounce ​

Fired from trackingUpdate when both are true:

  1. A feature change was recorded in updates[].
  2. lastChange.time - tracking.lastSync > 3000 (3 second gap).

So it is not a fixed 3-second interval timer; rapid clicks may batch into fewer pushes.

Which interactions call trackingUpdate today:

UI pathic_update
Number inputs (FormFeatureNumberBox)Yes
Radio/checkbox via legacy SelectInput (form app) — list, image buttons, round buttons, carousel, image list, image slide and selection sliderYes
Other display widgets (dropdown, switch, color picker, text inputs)Not wired yet

ic_setup, ic_step and ic_finish still fire regardless. For feature-level GTM on dropdown/switch configurators, use ic_step for progress and ic_finish as the conversion event until those widgets emit updates.

ic_finish ​

Fired in trackingFinish when submit completes successfully (useSubmitQuote, cart flow, standalone contact form). Adds an update with changedFeature: 'info_form' and sets finished: true.

GTM setup example ​

1. Container snippet (head) ​

Standard GTM install — creates dataLayer.

2. Custom Event trigger ​

SettingValue
Trigger typeCustom Event
Event nameic_finish (conversion), ic_form_show (form view / funnel), or ic_update (engagement)

Repeat for ic_setup if you need a session-start tag.

3. Data Layer Variables ​

Create Data Layer Variable types in GTM:

Variable nameData Layer Variable Name
DL - product iddata.product_id
DL - finisheddata.finished
DL - total timedata.time.total

For the last feature changed, use a Custom JavaScript variable or map from data.updates in a tag template.

4. GA4 event tag (example) ​

SettingValue
Tag typeGoogle Analytics: GA4 Event
Event nameiconfigure_finish
TriggerCustom Event ic_finish
Event parameter product_idGTM variable DL - product id

Debug checklist ​

  1. Product has tracking enabled in Manager.
  2. Page uses script inject, not iframe.
  3. GTM Preview open; dataLayer exists before configurator loads.
  4. Console after load:
javascript
window.dataLayer.filter((e) => e.event?.startsWith('ic_'))
  1. Change a number feature → expect ic_update after debounce.
  2. Complete submit → expect ic_finish with data.finished === true.

Manager analytics vs GTM ​

SourceUse case
Manager DashboardFunnel, conversion, step drop-off (Supabase tracking)
Data → Export (Tracking)Raw export (CSV or JSON)
GTM / GA4Marketing tags on your domain

See Marketing dashboard for the Manager side.