Skip to main content
Use presentStudy(...) to prepare and open a hosted study for the current browser user. For the concepts behind this, see About presenting studies. These presentation APIs are part of the @trypillow/web package.

Present a study

await sdk.presentStudy({ id: 'demo-study' });
The SDK bootstraps the audience session, prepares the study, and opens the Pillow widget shell.

Study options

presentStudy accepts an options object as second argument:
OptionTypeDefaultDescription
skipIfAlreadyExposedbooleanfalseSkip presenting if the user has already seen this study. Emits studySkip instead.
forceFreshSessionbooleanfalseIgnore any saved session and start a new interview.
presentationobjectNoneLayout and appearance options (see below).
await sdk.presentStudy(
  { id: 'demo-study' },
  {
    forceFreshSession: true,
    skipIfAlreadyExposed: true,
    presentation: {
      mode: 'modal',
      width: 720,
      height: 720,
    },
  },
);

Presentation options

Control how the study window is displayed by passing presentation in the study options.
OptionTypeDefaultDescription
mode'modal' | 'overlay' | 'embed''modal'How the study is displayed.
position'bottom-right' | 'bottom-left''bottom-right'Corner position. Applies to overlay mode.
zIndexnumber999999CSS z-index for the widget container.
widthnumber | stringMode-dependentWidth of the study window. Numbers are pixels. Strings are used as-is (e.g. '80vw'). Defaults: 400px modal, 360px overlay/embed.
heightnumber | stringMode-dependentHeight of the study window. Defaults: 680px modal, 540px overlay, 520px embed.
borderRadiusnumber | stringMode-dependentCorner radius. Defaults: 24px modal/embed, 20px overlay.
bottomnumber | string20pxBottom offset. Applies to overlay mode.
topnumber | stringNoneTop offset. Applies to overlay mode.
leftnumber | stringNoneLeft offset. Applies to overlay mode.
rightnumber | stringNoneRight offset. Applies to overlay mode.
targetstring | HTMLElement | nullnullMount target for embed mode. Pass a CSS selector or a DOM element.
backdropobjectSee belowBackdrop overlay for modal mode.

Presentation modes

  • modal: Centered dialog over a full-page backdrop. Dismissed by clicking the backdrop or pressing Escape.
  • overlay: Floating corner panel anchored to position. Dismissed via the close button or Escape.
  • embed: Inline inside a target element. No backdrop or positioning; the study fills the target container.
If you omit mode, the SDK defaults to modal.

Backdrop options

Control the overlay behind the study window in modal mode.
OptionTypeDefaultDescription
blurnumber6Backdrop blur radius in pixels.
colorstring'#0f172a'Backdrop tint color (any CSS color value).
alphanumber0.42Backdrop opacity from 0 (transparent) to 1 (opaque).

Examples

// Modal with custom size and backdrop
await sdk.presentStudy({ id: 'demo-study' }, {
  presentation: {
    mode: 'modal',
    width: 720,
    height: 720,
    backdrop: { blur: 10, alpha: 0.55, color: '#020617' },
  },
});

// Overlay in the bottom-left corner
await sdk.presentStudy({ id: 'demo-study' }, {
  presentation: {
    mode: 'overlay',
    position: 'bottom-left',
    bottom: 32,
    left: 32,
  },
});

// Embedded inside a container
await sdk.presentStudy({ id: 'demo-study' }, {
  presentation: {
    mode: 'embed',
    target: '#study-container',
    width: '100%',
    height: '100%',
    borderRadius: 0,
  },
});

Launch studies

Launch studies are audience-targeted studies configured in the dashboard. Instead of hard-coding which study to show, call presentLaunchStudyIfAvailable() at a safe moment in your app and the backend decides which study (if any) to present.
await sdk.presentLaunchStudyIfAvailable();
If no launch study is pending for the current user, the call is a no-op. Good places to call it:
  • After the app finishes initializing and the main view is mounted
  • On route changes in a single-page app
  • After a user action where a modal or overlay is appropriate
The browser SDK does not auto-present launch studies. You must call presentLaunchStudyIfAvailable() yourself. The heartbeat only refreshes which study is pending; it never opens the widget on its own.

How it works

The SDK refreshes the pending launch study assignment whenever it heartbeats with the backend, on initialization and when the tab returns to the foreground (throttled to at most once per minute). presentLaunchStudyIfAvailable() uses the latest assignment and clears it after presenting.

Overriding server-configured display

When you configure a distribution in the dashboard, the server sends display settings (mode, size, backdrop) to the SDK. These are applied automatically. If you pass presentation options to presentLaunchStudyIfAvailable(), your values override the server defaults:
await sdk.presentLaunchStudyIfAvailable({
  skipIfAlreadyExposed: true,
  presentation: {
    mode: 'overlay',
    position: 'bottom-right',
  },
});

Events

Launch studies emit the same lifecycle events as manual presentStudy() calls: studyPresent, studySkip, studyFinish, and studyError.
To set up audience targeting and launch distributions, see the audience targeting guide.

Distribution

You can ship the browser SDK in either of these ways:
  • Import @trypillow/web from your frontend app.
  • Load https://unpkg.com/@trypillow/web/dist/pillow-sdk.min.js with a script tag.

Lifecycle events

Register listeners before calling presentStudy(...) to capture every event.
EventPayloadWhen
studyPresent{ study }The study widget has opened.
studySkip{ study }The study was skipped (e.g. skipIfAlreadyExposed matched).
studyFinish{ study }The participant completed the study.
studyError{ study, error }Something went wrong preparing or presenting the study.
sdk.on('studyPresent', ({ study }) => {
  console.log('Presented', study.id);
});

sdk.on('studyFinish', ({ study }) => {
  console.log('Finished', study.id);
});

sdk.on('studySkip', ({ study }) => {
  console.log('Skipped', study.id);
});

sdk.on('studyError', ({ study, error }) => {
  console.error('Failed', study.id, error);
});

await sdk.presentStudy({ id: 'demo-study' });

Unsubscribing

on(...) returns an unsubscribe function. Call it when you no longer need the listener, for example when a component unmounts.
const unsubscribe = sdk.on('studyFinish', ({ study }) => {
  console.log('Finished', study.id);
});

// Later, when you no longer need this listener:
unsubscribe();
You can also remove a specific listener with off(...):
function handleFinish({ study }) {
  console.log('Finished', study.id);
}

sdk.on('studyFinish', handleFinish);

// Later:
sdk.off('studyFinish', handleFinish);

What’s next

Audience targeting

Target specific user cohorts with automatic study launches.

Getting started

Review the full browser SDK setup flow.

Session management

Clear or reset sessions when the user logs out or restarts a study.