Variables & Personalization

React to user attributes, device state, and placement parameters — and write paywalls the dashboard can experiment on without a rebuild.

Everything your app and the SDK tell a paywall about the presentation arrives through useVariables(): who the user is, what device they're on, and what the placement was called with. Read these defensively and a single paywall can greet a returning user by name, adapt to platform, or react to any parameter your app passes — all without a rebuild.

useVariables()

import { useVariables } from "superwall/hooks";

const { device, user, params } = useVariables();

Three records, three sources:

  • device — filled in by the SDK: platform, deviceModel, osVersion, appVersion, deviceLocale, regionCode, deviceCurrencyCode, subscriptionStatus, activeEntitlements, daysSinceInstall, totalPaywallViews, and more.
  • user — whatever your app set via setUserAttributes (user.firstName, user.plan, …).
  • params — whatever the placement was called with (params.placementName, plus anything the app passed alongside it).
const name = typeof user.firstName === "string" ? user.firstName : undefined;

<h1>{name ? `Welcome back, ${name}` : "Go Pro"}</h1>
<span>{device.platform ?? "—"}</span>

Guard every read

All three records are filled in by the host — your paywall controls none of them, so every read needs a fallback:

  • For device fields, ?? "—" (or any sensible default) suffices — the SDK guarantees the shape, just not that a value has arrived yet.
  • For user and params, the host controls the type too, so check it before using it: typeof params.placementName === "string". An attribute your app sets as a number today might be a string tomorrow, and the paywall must not crash either way.

device.isSandbox is a string, not a boolean. Compare it as one.

While previewing, every one of these values is editable live in the studio's Variables panel — user attributes, device properties, placement params, and per-product variables — seeded from your app's real sample data. Change a value and watch the paywall react. See The studio.

useUser()

Shorthand for when you only need the user record:

import { useUser } from "superwall/hooks";

const user = useUser();

Identical to useVariables().user — reach for it when the device and params records aren't needed.

useDevice()

The same device record as useVariables().device, plus orientation:

import { useDevice } from "superwall/hooks";

const { orientation, platform, deviceModel } = useDevice();

orientation is "portrait" | "landscape", measured in the page itself — it updates the moment the device turns, so you can build layouts that answer to rotation. The orientation example reflows to a two-column grid in landscape rather than shrinking the portrait layout; see Examples.

Built to be experimented on

Notice what's missing: variables are never declared in code. What the paywall reads — user attributes, device state, placement params, product variables, trial eligibility — is supplied by the app and the store at runtime, and the studio overrides all of it live while previewing.

Write every read defensively — guarded, typed, with a designed fallback — and every one of those values becomes a knob the dashboard can turn without a rebuild. A paywall that renders sensibly for any combination of inputs can be A/B tested freely.

The personalization example shows the full doctrine in one project: ?? "—" for SDK-guaranteed device fields, typeof checks for host-controlled user and params reads, and designed fallbacks for every string. See Examples.

How is this guide?

On this page