Skip to Content

State

Every state export of @appfunnel-dev/sdk, generated from the SDK source. Signatures and descriptions are exact; for how these fit together, see the guides.

useClickIds

hook · state/namespaces.ts

useClickIds(): FunnelContext["clickIds"]

useClickIds() — per-network ad click-ids captured on entry (read-only).

useContextValue

hook · state/namespaces.ts

useContextValue(path: string): unknown

Read a dotted path out of context, e.g. useContextValue('device.isMobile').

useData

hook · state/namespaces.ts

useData(key: string): [VariableValue, (value: VariableValue) => void]

const [score, setScore] = useData('score') — working/scratch state. Persisted (survives reload/return) but kept out of responses aggregation.

useDevice

hook · state/namespaces.ts

useDevice(): FunnelContext["device"]

useDevice() — viewport, device type, pixel ratio (read-only).

useField

hook · state/namespaces.ts

useField(path: string): { value: VariableValue; set: (value: VariableValue) => void; }

The headless field hook — useField('responses.goal') — the building block the styleable components (<Choice>) wrap. Takes a namespaced path and returns { value, set }. Accepts only writable namespaces.

useFunnel

hook · state/namespaces.ts

useFunnel(): Funnel

The imperative handle. Does not subscribe — use it in event handlers and effects (useFunnel().responses.set({...})). For reactive reads use the hooks.

useLocale

hook · state/namespaces.ts

useLocale(): FunnelContext["locale"]

useLocale() — resolved locale/language/region/timezone (read-only).

useMarketingConsent

hook · state/namespaces.ts

useMarketingConsent(): [boolean, (granted: boolean) => void]

const [consent, setConsent] = useMarketingConsent() — the visitor’s marketing opt-in as a plain boolean. Writing true records the answer AND fires the platform marketing.consent_given event, which sets the customer’s consent flag and forwards marketing_consent: true to marketing integrations (Customer.io). Writing false records the answer only — consent is one-way; there is no un-consent event today. Prefer this over useResponse('marketingConsent'), which records the variable but never drives the consent pipeline.

useMeasurementSystem

hook · state/measurementSystem.ts

useMeasurementSystem(): MeasurementSystem

Seed a funnel’s unit toggle from the visitor’s likely-expected measurement system. SSR-safe: 'metric' on first paint (server + hydration), resolved post-mount. See the module JSDoc for the rule, the UK decision, and the detection priority.

useResponse

hook · state/namespaces.ts

useResponse(key: string): [VariableValue, (value: VariableValue) => void]

const [goal, setGoal] = useResponse('goal') — one funnel answer, useState-shaped and per-key reactive. Writing persists + (later) feeds analytics aggregation.

useSystem

hook · state/namespaces.ts

useSystem(): FunnelContext["system"]

useSystem() — funnel id, test/live mode, build clock (read-only).

useUserAttribute

hook · state/namespaces.ts

useUserAttribute(key: string): [VariableValue, (value: VariableValue) => void]

const [height, setHeight] = useUserAttribute('height') — any field on the user. The platform routes known fields → typed column, custom → attribute; the author doesn’t specify which. email is a plain attribute like any other — writing it does NOT identify; complete identity explicitly on submit with useFunnel().completeUserRegistration().

useUtm

hook · state/namespaces.ts

useUtm(): FunnelContext["utm"]

useUtm() — the 5 utm params captured on entry (read-only).

FunnelProvider

component · state/namespaces.ts

FunnelProvider({ config, sessionValues, deferSessionValues, tracker, context: contextOpts, offerings, experiments, checkout, messages, locale: localeOverride, toaster, store, children, }: FunnelProviderProps): ReactNode

Mounts a funnel: builds the writable VariableStore + the read-only FunnelContext (once) and provides the runtime to the tree via hooks.

buildAcquisition

function · state/context.ts

buildAcquisition(context: FunnelContext): Acquisition

Assemble the full Acquisition snapshot from the context + document (cookies, referrer, path) — captured once on entry, forwarded to the tracker for first-class persistence (session + first-touch Customer) + CAPI.

buildContext

function · state/context.ts

buildContext(opts?: BuildContextOptions): FunnelContext

Build the read-only FunnelContext once, at mount. page starts at the first page and is replaced per navigation when assembling a snapshot.

createFunnelStore

function · state/store.ts

createFunnelStore(config?: FunnelStoreConfig, sessionValues?: Record<string, VariableValue>): VariableStore

Seed a store from a funnel’s declared responses/data defaults, then layer server / session-restored values on top (highest priority).

measurementSystemForCountry

function · state/measurementSystem.ts

measurementSystemForCountry(country: string | null | undefined): MeasurementSystem | null

Pure rule: map a country/region code to a measurement system. Returns null when the input is absent or not a 2-letter code (so the caller can fall through to the next source); a known metric country returns 'metric', not null.

resolveMeasurementSystem

function · state/measurementSystem.ts

resolveMeasurementSystem(contextRegion?: string | null): MeasurementSystem

Pure resolver over the priority chain: context region → navigator.language region → 'metric'. Reads navigator (client-only) — call it in an effect, not during render (see the hook). Exported for direct/unit use.

Namespace

class · state/namespaces.ts

Namespace: typeof Namespace

A typed view over one writable namespace’s slice of the store.

VariableStore

class · state/variableStore.ts

VariableStore: typeof VariableStore

Acquisition

interface · state/context.ts

export interface Acquisition { utm: UtmContext clickIds: ClickIdContext cookies: { fbp?: string; fbc?: string } referrer: string landingPath: string }

The full acquisition snapshot captured once on entry — the funnel reads utm / clickIds off FunnelContext; the SDK forwards this whole thing to the tracker (setAcquisition) so the platform persists it first-class: per-visit on the session + first-touch (write-once) on the Customer, and the click-ids / _fbp/_fbc cookies feed CAPI. Not stored as a generic attribute/answer.

BrowserContext

interface · state/context.ts

export interface BrowserContext { name: string version: string userAgent: string language: string cookieEnabled: boolean online: boolean }

BuildContextOptions

interface · state/context.ts

export interface BuildContextOptions { funnelId?: string campaignId?: string mode?: 'test' | 'live' now?: number sessionId?: string | null customerId?: string | null visitorId?: string | null overrides?: Partial<FunnelContext> }

ClickIdContext

interface · state/context.ts

export interface ClickIdContext { fbclid?: string gclid?: string gbraid?: string wbraid?: string msclkid?: string ttclid?: string twclid?: string sccid?: string li_fat_id?: string epik?: string rdt_cid?: string [key: string]: string | undefined }

Per-network ad click-ids captured from the entry URL — the matching signals the server CAPI needs. Named per network (not a generic bag) so the platform + each integration map them precisely; [key] catches any others.

DeviceContext

interface · state/context.ts

export interface DeviceContext { type: 'mobile' | 'tablet' | 'desktop' isMobile: boolean isTablet: boolean screenWidth: number screenHeight: number viewportWidth: number viewportHeight: number colorDepth: number pixelRatio: number }

v2 context — the read-only, computed namespace.

Everything the funnel can read but never write: the device, the visitor’s locale, the current page, acquisition params (UTM), the session, and system facts (funnel id, test/live mode, clock). It supersedes the v0 flat page.*/device.*/browser.*/os.*/query.*/metadata.* system variables (see ./systemVariables) by folding them into one nested object.

Why it’s not in the ./variableStore: the store holds writable state (user/responses/data) that persists + re-renders per key. Context is derived and read-only, so it lives as a plain object on a React context — no subscriptions, no churn. The one moving part (page) is merged into the snapshot from navigation state at read time.

Authors read it through hooks (useDevice(), useLocale(), usePage(), useUtm()) or inside routing/condition predicates as s.context.*.

Funnel

interface · state/namespaces.ts

export interface Funnel { user: Namespace responses: Namespace data: Namespace readonly context: FunnelContext readonly experiments: RuntimeExperiment[] track: Tracker['track'] completeUserRegistration(email?: string): boolean snapshot(): FunnelSnapshot }

The imperative funnel handle (useFunnel()). Non-reactive — for event handlers and effects. Reactive render reads go through the hooks instead.

FunnelContext

interface · state/context.ts

export interface FunnelContext { device: DeviceContext browser: BrowserContext os: OsContext locale: LocaleContext page: PageContext utm: UtmContext clickIds: ClickIdContext session: SessionContext identity: IdentityContext system: SystemContext }

The whole read-only computed namespace, as read in predicates: s.context.*.

FunnelProviderProps

interface · state/namespaces.ts

export interface FunnelProviderProps { config?: FunnelDefinition sessionValues?: Record<string, VariableValue> deferSessionValues?: boolean tracker?: Tracker context?: Parameters<typeof buildContext>[0] offerings?: OfferingInput[] experiments?: RuntimeExperiment[] checkout?: CheckoutDriver messages?: MessageCatalog locale?: string toaster?: boolean | ToasterProps store?: VariableStore children: ReactNode }

IdentityContext

interface · state/context.ts

export interface IdentityContext { customerId: string | null visitorId: string | null }

Durable identity for stable experiment bucketing (phase-4 §4.2). The preferred seed is the signed anonymous visitorId (stable from the first pageload, matching the server-side FUNNEL split in resolve.ts); customerId is only the fallback for cookieless SDK embeds. visitorId-FIRST is deliberate — the anon customerId is minted server-side AFTER the first tracked event, so seeding on it would flip a returning visitor’s arm between visit 1 and 2 (see ../flow/experiments.bucketingSeed). It is deliberately not the session id or fingerprint — a returning visitor (e.g. via winback) must keep their variant across sessions (landmine #4). Both null = unidentifiable traffic, and the experiment layer falls back to control rather than bucket on nothing.

LocaleContext

interface · state/context.ts

export interface LocaleContext { locale: string language: string region: string timeZone: string }

OsContext

interface · state/context.ts

export interface OsContext { name: string timezone: string }

PageContext

interface · state/context.ts

export interface PageContext { key: string slug: string group?: string index: number total: number progressPercentage: number startedAt: number }

SessionContext

interface · state/context.ts

export interface SessionContext { id: string | null startedAt: number referrer: string landingPath: string }

SystemContext

interface · state/context.ts

export interface SystemContext { funnelId: string campaignId: string mode: 'test' | 'live' now: number }

UtmContext

interface · state/context.ts

export interface UtmContext { source?: string medium?: string campaign?: string content?: string term?: string [key: string]: string | undefined }

Acquisition / ad params captured on entry (the old query.* utm slice).

MeasurementSystem

type · state/measurementSystem.ts

export type MeasurementSystem = 'imperial' | 'metric'
Last updated on