Jump to

SDK: Showing surveys

Survey modes

The root option picks the mode.

Comparison table
Popup (default)Embed
How to get itinit() without rootinit() with root: a CSS selector or an element
Where it showsFloating card in the bottom-right cornerInside your container, in the page flow
When it showsBy the dashboard rules: see When a popup showsRight after init(), no triggers
Close buttonYesNo
Feedback buttonOptional (dashboard setting)No
On SPA navigationClosed and set up again for the new URLStays
Several at onceOne open, the rest wait in a queueYes, one per container
CallbacksonRender, onSubmit, onCloseonRender, onSubmit
open() / close()YesNo
// popup
ftools('init', {
  surveyId: '__SURVEY_ID__',
  publishableKey: '__PUBLISHABLE_KEY__'
});

// embed, by selector
ftools('init', {
  surveyId: '__SURVEY_ID__',
  publishableKey: '__PUBLISHABLE_KEY__',
  root: '#my-container'
});

// embed, by element
ftools('init', {
  surveyId: '__SURVEY_ID__',
  publishableKey: '__PUBLISHABLE_KEY__',
  root: document.getElementById('my-container')
});

Embed notes:

  • The SDK looks up root after the survey data loads, in document only. It does not look inside another shadow root.
  • One container holds one survey.
  • When you remove the container from the DOM, the SDK cleans the survey up.

When a popup shows

The survey's Settings → Triggers tab decides when a popup opens. Embed surveys ignore these settings.

Manual event tracking picks who opens the popup:

Comparison table
Manual event trackingWhat opens the popupopen()
Off (default)The trigger: right after init() when no trigger is set, after a delay in seconds, or after a scroll depth in percentDoes nothing
OnOnly your codeOpens the popup

A scroll trigger fires when the visitor scrolls past the given percentage of the page. A page too short to scroll counts as 100%.

Target pages (Where to show this survey → Specific pages) limit the popup to some URLs. The SDK compares each pattern with the whole path, query and hash of the current URL, and * matches anything. So /pricing does not match /pricing?plan=pro; use /pricing* for that. With manual event tracking on, target pages don't apply: your code decides.

Two settings stop a popup from coming back:

Comparison table
SettingEffect
Display → Don't show again if dismissedOn: after the visitor closes the popup, it doesn't show to them again. Off: it shows again on the next page load or SPA navigation.
Repeat responses → Allow the same respondent to answer againOff: a visitor who answered doesn't see the survey again. On: they can see it and answer again.

Both rules recognize a returning visitor by the session the SDK keeps in localStorage (see Privacy and data). The answer rule also recognizes them across sessions by userId, if you pass it; the dismiss rule does not. open() with force: true ignores both rules.

One popup at a time. If several popup surveys are due, the first one opens and the rest wait in a queue. The next one opens when the current one closes.

Feedback button. With Show feedback button in bottom-right corner on, a button on the right edge of the page, near the bottom, opens the survey on click. It shows only while no popup is open.

Single-page apps

The SDK follows URL changes made with history.pushState, history.replaceState and the back and forward buttons. On every change of the path, query or hash:

  • open popups are removed without onClose;
  • triggers and target pages are checked again for the new URL;
  • embed surveys stay as they are.

The SDK wraps the History API methods it finds at load time, so a router that patches them earlier keeps working.