Miniwall Docs
SDK

Present a screen

Open a screen over your page or app and get a result when the user buys, restores, closes, acts or is skipped.

present opens a screen over your page or app and finishes when the user is done with it. Use it for upgrade buttons, feature gates, prompts and any moment your own code decides.

function presentPaywall(options: PresentOptions): Promise<PurchaseResult>;
import { configure, presentPaywall } from '@ui-kit/web';

configure({ apiKey: 'pk_...' });

const result = await presentPaywall({
  paywall: { placement: 'upgrade_button' },
  products: {
    monthly: { price: 9.99, currency: 'USD', period: 'month' },
    annual: {
      price: 59.99,
      currency: 'USD',
      period: 'year',
      offer: { price: 0, period: { unit: 'day', count: 7 } },
    },
  },
  onPurchase: async ({ packageId }) => startCheckout(packageId),
});

Where the screen comes from

SourceLoadsAnalytics
Placement (web { placement }, iOS .placement, Android ScreenSource.Placement)The screen the placement shows now: its A/B test, schedule, audiences, frequency limit, holdout and display format apply. Recommended. Needs the API key.Yes
Paywall ID ({ paywallId }, .paywallId, ScreenSource.PaywallId)That screen's published version. Needs the API key.Yes
JSON (.json, ScreenSource.Json, { json }; on web the JSON object itself)A screen JSON you bundle, such as a file from Download JSON on the Screens page.None
URL (web only)JSON from a URL string.None

See Preload and sources for the JSON format and caching. A JSON source without components_config.base fails: Miniwall: not a screen JSON (expected components_config.base.stack) on mobile, Not a paywall JSON: expected components_config.base on web.

Options

Every option is optional (on web, paywall is required).

Source and look

OptionTypeDefaultWhat it does
paywallsourcerequiredWeb only: the source (placement, paywall ID, URL or JSON). On mobile the source is the first argument.
mode (web) / style (mobile)see Display modesthe placement's format, else web modal, mobile full screenFormat. When you pass it, the placement's format (and on web its position, size and duration) is ignored.
position, size, duration, closeOnBackdrop, maxContentWidthsee Display modesWeb only.
colorModelight, dark, systemfrom configure, else web light, mobile systemColour scheme.
localestringfrom configure, else the screen's default (web) or the device's (mobile)Language, such as vi_VN (vi-VN also works). If the screen lacks it, a language with the same first part is used, then the screen's default.
screensmap of screen ID to JSON (web: or URL)noneJSON and URL sources only: the screens a "Go to another screen" button can open. Placement and paywall ID sources fetch linked screens from Miniwall.

Prices and text

OptionTypeWhat it does
productsmap of package ID to ProductInfoA price for each package ID. Fills every {{ product.* }} text. See Purchases and prices.
variablesPerPackagemap of package ID to a map of text valuesExtra {{ product.* }} values per package. They override the computed ones.
customVariablesmap of strings, numbers or booleansValues for {{ custom.name }}. Shown as plain text, never as HTML.

Callbacks

OptionCalled whenReturn value
onPurchaseThe user taps a purchase button. Receives packageId, product, answers (web).true or nothing: purchased. false: cancelled, the screen stays open. Throw: failure, the screen stays open and onError is called.
onRestoreThe user taps Restore purchases. The button only appears when you pass this.true or nothing: restored. false: nothing restored, the screen stays open.
onActionA "Run your code" button is pressed. Receives actionId, answers.false keeps the screen open. Anything else closes it.
onAnswerThe user changes an answer.none
onCloseWeb only: the visitor closes the screen.none
onSkipWeb only: targeting chose to show nothing. Receives reason.none
onOpenUrlA link is pressed. Default: new tab (web) or system browser (mobile).none
onErrorSomething failed after the screen opened.none

Details: Callbacks.

Targeting

OptionWhat it does
attributesWhat you know about the user, merged over the ones from configure and setAttributes for this call. Used by the placement's audiences. Never sent to Miniwall. See Targeting attributes.

The result

ResultWeb, React Native, Flutter (outcome)iOSAndroid
Purchasedpurchased (packageId).purchased(packageId, answers)PurchaseResult.Purchased
Restoredrestored.restored(answers)PurchaseResult.Restored
Closedclosed.closed(answers)PurchaseResult.Closed
Actionaction (actionId).action(actionId, answers)PurchaseResult.ActionTaken
Skippedskipped (reason).skipped(reason)PurchaseResult.Skipped

answers holds what the user answered (see Answers). skipped means nothing opened; reason is frequency_cap, audience or holdout.

present throws (or the promise rejects) when the screen cannot be loaded, for example a wrong key or no network and no saved copy. Wrap the call in try/catch. See Errors.

try {
  const result = await presentPaywall({ paywall: { placement: 'upgrade_button' }, products, onPurchase });
  switch (result.outcome) {
    case 'purchased': unlockPro(result.packageId); break;
    case 'action': handleAction(result.actionId, result.answers); break;
    default: break; // closed, restored or skipped
  }
} catch (error) {
  console.error('Could not show the screen', error);
}

While it is open

  • While your onPurchase, onRestore or onAction runs, the screen shows a busy spinner and ignores further taps, so a double tap cannot start two checkouts.
  • A screen with "Go to another screen" buttons moves between screens inside the same presentation. The result arrives once, when the flow ends. The first screen is the one the placement returns, so its A/B test, schedule and audiences apply to the whole flow. See Answers.
  • The SDK adds no close button. A screen closes with a button in its design that uses the action Close the screen, or with the platform gestures below. Closing finishes with closed.
  • Links open only if they are http, https, mailto, tel or start with a single /.

Differences

Web: only one screen is presented at a time; presenting another closes the current one and the earlier promise resolves with closed. Esc closes the screen (for a banner, slide-in or toast only when focus is inside it). Popups, full screens and drawers lock page scroll, trap focus (Tab wraps) and restore focus on close. A modal's backdrop click closes it unless closeOnBackdrop: false.

iOS: swipe down closes a sheet, but the swipe is blocked while onPurchase, onRestore or onAction runs, so a result is never lost. A full-screen cover has no gesture. If nothing can be presented (no view controller or window, or UIKit is busy presenting or dismissing), onError gets the reason and present finishes with closed.

Android: tap outside a sheet or system back closes it; back closes an open sheet inside the screen first, then the screen. Full screen closes with system back.

With a placement

A placement source also applies the placement's setup in the dashboard: which screen, its A/B test, a schedule in force, audiences, frequency limit, holdout and display format. See Placements. Views, closes, purchase taps, purchases, restores, button presses and chosen answers are reported to Analytics unless you set analytics to false. See Events and privacy.

On this page