Build an on-site widget — banner, popup, slide-in or multi-step form — decide who sees it and when, then publish it. Until you press Publish, nothing here is visible to anyone.
It all lives at /admin/interaction-widgets ("Widget Builder"), and you need the ADMIN role.
Pick a surface, not a "form type"
New widget offers nine types: Banner, Popup modal, Slide-in, Big sidebar, Fullscreen, Standalone page, Embedded widget, Site widget (a floating launcher visitors open themselves) and Interactive video.
"Multi-step" is not one of them. Any surface can carry several steps, so a three-step qualifying form can be a modal, a slide-in or a standalone page. Pick the surface here; steps come later.
The widget is created straight away as a draft named "Untitled popup modal" or similar, with one trigger pre-set: banners get Immediately, everything else After time on page at 3 seconds.
Six tabs: Steps, Design, Display, Targeting, Schedule, Actions. Interactive video is the one exception — it drops Display and gains a Video tab in its place, where you set the source before building cues on the timeline.
Embeds, standalone pages and site widgets do keep their Display tab, but it carries no triggers. Open it and you get an explanation instead: this surface isn't fired by a trigger at all, and its visibility comes from its status, its Schedule and its Targeting.
Three types you place rather than trigger
Standalone page. Its URL is on the Design tab under Page URL: a base dropdown (/ (root), /forms, /myarea) plus a slug — nothing else is routable. As you type, the field says either "· Available" in green or a reason in red: names clashing with a reserved route (admin, blog, products, events, login and about twenty more at root), with an existing page, or with another standalone form. That red text is a warning, not a block — nothing stops you publishing a clashing path, and the real page or route wins, so the widget simply never appears there. Fix it before you publish. And the URL returns nothing until the widget is published, including for you — typing it in yourself looks exactly like a wrong path, so use Preview on site until it's live.
Embedded widget. Place it from the widget picker in the page, blog-post and lesson editors, from an Embed section set to Widget in the site-page builder, or as an Embedded widget element inside another widget. Where it's used lists placements in pages, blog posts, lessons and host widgets — it does not find an embed sitting in an Embed section — on any builder page, so structural pages, custom pages, product pages and event pages alike, because all four store the widget in the section's own config rather than in page HTML. Check those by hand before you unpublish or delete one. Targeting differs here too: Reached a conversion is ignored, so such an embed shows to everyone, and segments come from a stored list refreshed every few minutes rather than evaluated live — a segment of anonymous visitors never matches an embed.
Site widget. Its launcher shows on every page the widget qualifies for; triggers are ignored. On the Design tab you choose what the launcher does: show this widget's own content, in which case Opens as picks the surface (modal, slide-in, sidebar or fullscreen) — or open a different widget entirely, in which case the Design tab styles only the round launcher button and the widget you point at keeps its own design.
Build the content on the Steps tab
A step holds an ordered list of elements, added from Add element: Headline, Body text, Image, Video, Audio, Countdown, CTA button, Form field, Divider, Spacer, Embedded widget. Drag to reorder.
A new widget starts with one step. Add step, beside the step dropdown at the top of the tab, creates the next one — that is what turns a widget into a multi-step form, and it is the precondition for branching further down. The dropdown itself switches which step you're editing, or shows All steps at once. Each step card also offers Duplicate step and Delete step; the last remaining step can't be deleted.
A Form field has a Kind (text, email, phone, textarea, select, radio, checkbox, consent, hidden) and a Map to contact setting: (store only), email, fname, lname, phone or tag. Only mapped fields update the contact record; the rest are stored as answers on the submission.
Once a step collects an email, the next field you add arrives as a ready-made consent checkbox rather than a text field. It is required, always renders unticked and cannot be pre-ticked. Its wording is a starting point, not a legal text — edit it to name what you will actually send.
Conditional flow, per step, is where branching lives — on an answer ("if Interest = Pricing, jump to step 3") or on a CTA that jumps. Order matters: Add branch is disabled until a second step exists, and the answer-driven option until the step has a radio, select or checkbox field. Branches evaluate top-down, first match wins, no match advances linearly; one aimed at a deleted step falls through to the next step.
The builder adds no Back button — that's a CTA with "Go to previous step" — and a step whose elements you delete renders as an empty body rather than falling back to anything.
CTAs: what the button does
A CTA's actions come from three grouped menus: Navigation (next / previous / jump to a step, open a link, open another widget, close), Side effects (tags, permissions, automations, asset downloads, confetti) and System (cookie settings, log in / out, My Area, referrals, testimonials, Knowledge Base).
Stack several on one button and the side effects all run in order. But with more than one navigation action, only the last takes effect: "Add tag" + "Open a link" works as expected, while "Go to next step" + "Close widget" quietly does only the close (the editor warns). A System action fixes the button's text and icon, so the Label field goes read-only.
Each CTA has a Mark as conversion switch. Left alone it derives: a CTA that opens a link always counts, and on the last step so does "next step" or "close". Nothing else does — a mid-flow "Continue" records no submission, though its tags and automations still run.
A field mapped to email is checked server-side, domain included; a bad address blocks the conversion with "Please enter a valid email address." A good one creates a contact as a LEAD, or fills only the blank fields of an existing one.
The "On dismiss" editor does nothing. The Actions tab offers widget-level actions for when a visitor closes without converting. They save and publish, but nothing runs them — set "add tag on dismiss" and no tag is added.
Design: how it looks
Position lives here, per type — top or bottom for a banner, a corner for a slide-in, left or right for a sidebar — with corner radius, desktop width (mobile is always full width), control size and the overlay behaviours: close button, scroll lock, close on outside click or Esc.
Colours are up to three tokens, each with a light and a dark value: Accent, Card bg, and a backdrop row — labelled Page background on a standalone page, Overlay background everywhere else. How many you get depends on the type. A banner, slide-in or embed paints no backdrop of its own, so it shows only the first two; a modal, fullscreen, sidebar, site widget, interactive video or standalone page shows all three. A site widget set to open a different widget styles only its launcher button, so it shows Accent alone.
Leave a value blank and it falls back to your workspace preset, then a built-in default. There is no per-widget light/dark switch: a widget follows the site theme of whoever is looking, which is what the dark values are for. You cannot force a dark popup onto a light site.
Display: when it appears
Set Show when to Any trigger or All triggers (every one must arm first), then add triggers:
| Trigger | What it does | Default if left blank |
|---|---|---|
| Immediately | Fires on page load | — |
| After time on page | Waits N seconds | 0 seconds |
| After scroll % | Scrolls past N% | 50% |
| On exit intent | Pointer leaves the top of the viewport | — |
| On click of selector | A CSS selector is clicked (e.g. #buy) |
— |
| After inactivity | N seconds without interaction | 20 seconds |
| Previous page visited | A page matching a pattern was seen this session | — |
| After N pages viewed | Pages viewed this session | 3 |
| After N clicks | Clicks on the current page | 3 |
Exit intent is desktop-only in practice. It watches for the mouse leaving the top of the window; nothing handles mobile back-navigation, so on phones it never fires.
No triggers means "never opens by itself", not "always show" — a legitimate setup. Such a widget opens when another widget's "Open another widget" CTA points at it, or when a link aimed at it is clicked. Link fields that use the shared link picker — nav items and nav CTAs, footer links, section buttons — carry a Widget button beside Pick page and Section. Not every link field does: a few plainer ones (a logo-strip item's link, a team member's link) are bare URL boxes with no picker, so paste the widget's link there yourself.
Targeting: who sees it
Audience: Everyone, Anonymous visitors only, Known contacts only, In a segment (contacts or visitors), Reached a conversion (Goal / Micro / Trigger). Segments and conversions come from /admin/audiences and are evaluated live on each page view, so someone qualifies the moment they meet the criteria. A segment with no conditions matches nobody — a widget aimed at it never appears anywhere, a common cause of a silently invisible widget.
Capture URL / UTM parameters is on by default: every query parameter on the page (utm_*, gclid, your own flags) is stored with each submission, cut at 512 characters.
Consent category (Functional / Analytics / Marketing / Remarketing) makes the widget wait until the visitor grants that cookie-consent category. Leave it on No consent required unless you need it — a widget waiting on a category nobody grants never shows.
Page targeting takes Show on, Hide on and required query params. Patterns are globs where only * is special; an empty Show on means every page. Hide on is checked first and always wins. Locale prefixes are stripped, so /pricing also covers /de/pricing.
Schedule, frequency and collisions
Start and End datetimes, Max shows per visitor (0 = unlimited), Cooldown (hours), Once per session, Stop showing after conversion (on by default) and Priority (higher wins).
Frequency capping is per-browser, not per-person. The counters live in cookies named wgt_… and session storage on the visitor's device, so clearing them, opening a private window or moving from phone to laptop resets every cap. The server does not enforce them; treat them as good manners, not a guarantee.
One setting overrides all of them. A Countdown element whose When timer hits zero is Hide widget forever (for this visitor) writes a permanent cookie checked before anything on the Schedule tab — the easiest way to make a widget vanish for yourself mid-test and conclude it is broken. A private window is the way back. An evergreen countdown also cookies its deadline, so a reload resumes the old timer.
Only one widget of each non-banner type can be open at a time, the higher Priority one winning; a modal and a slide-in can be open together, and banners stack at the same edge.
Real scheduling is: publish first, then set Start / End. Only active widgets are served, so a widget sitting on any other status — including the blue scheduled badge — shows to nobody.
Publishing
There is no Save button. Changes autosave to the draft about a second after you stop typing, and never touch the status, so editing can't push a widget live by accident. The flip side is what people get wrong most: editing a live widget changes nothing for visitors. The public site renders a frozen snapshot taken at publish time, not your draft.
The button by the status badge is three-state:
- Publish — not live. Snapshots the draft and sets it active.
- Republish — live, but your draft has moved on. Visitors see the old version until you press this.
- Unpublish — live and in sync. Takes it down.
Each opens a confirmation first. List rows are colour-coded: green = live and in sync, amber = live with unpublished edits, plain = draft. Amber is your reminder that you edited and forgot; renaming deliberately does not turn a row amber. Nobody needs to refresh — open pages pick up a publish within moments, though a widget already on screen keeps the version it opened with.
Unpublish pauses the widget and discards the published snapshot but leaves your draft alone, so Publish restores it. Pause one while someone is halfway through a multi-step form and that visitor can still finish; only new starts are refused.
Before publishing, use Preview on site: a real page of your site with the widget armed under its real triggers. Change the Page box to preview elsewhere, press Trigger widget when a trigger is hard to reproduce, and open Data collected to see what a real submission would have sent. Status, page rules, frequency and consent are bypassed, and nothing is recorded.
Variants, results and deleting
Beside the live preview is a Variant menu. Add one and the widget becomes an A/B test. The new arm is a full copy of the arm you cloned it from — design, steps, triggers, targeting, page rules, frequency and actions, all duplicated — not a thin overlay on top of A. So later edits to A do not flow into B: once you've split, every arm is a separate thing you maintain by hand, and a wording fix has to be made in each one. Manage variants sets the weights and the split: Sticky (a visitor keeps one arm) or Random (re-rolled each page load).
Duplicate silently drops variants. It copies design, steps, triggers, targeting, page rules, frequency, actions, schedule and priority — but the copy comes back single-arm, with no warning.
Responses live in the Analytics tab beside the widget list — filter by widget and date range for views, submissions and conversion rate — and the list icon on a row shows that widget's submissions directly. Each reads completed, in progress or abandoned (no activity for 30 minutes). The conversions counter counts clicks, not people: one visitor can fire several conversion CTAs in a session and each is recorded.
Deleting a widget deletes its submissions, permanently, and there is no export — if the responses matter, copy them out of Analytics first.
Comments