SDK Reference

Full API reference for the Loopana SDKs.

Packages

PackageDescription
@loopana/reactReact provider and hooks
@loopana/jsBrowser client for any JS framework
@loopana/nodeNode.js server client
@loopana/coreTypes and pure utilities (isomorphic)

@loopana/react

LoopanaProvider

Wrap your app to initialize the SDK and fetch all slot data in a single request.

import { LoopanaProvider } from "@loopana/react"

<LoopanaProvider
  apiKey="pk_live_xxx"
  userId={user.id}
  locale="en"
>
  {children}
</LoopanaProvider>

Props:

PropTypeRequiredDescription
apiKeystringYesProject API key from Settings > API Keys
userIdstringNoStable user ID for targeting and A/B test selection
localestringNoLocale code for translated content (e.g., fr, en-US)
baseUrlstringNoOverride the API base URL
eventsUrlstringNoOverride the events API base URL
appOriginstringNoYour app's origin (e.g., https://myapp.com). Used to detect internal vs external URLs.
dismissDurationDaysnumberNoDefault number of days a dismissed message stays hidden across all slots. Omit for an infinite dismiss. Override per slot via useSlot.
onError(err: unknown) => voidNoCalled when a background operation (e.g., trackEvent) fails.

The provider fetches all slots and messages for your project on mount. The data is then available via hooks.

useSlot

Hook to access messages for a specific slot.

import { useSlot } from "@loopana/react"

const { message, messages, dismiss, isLoading, error, trackEvent } = useSlot("homepage-banner")

Parameters:

ParameterTypeDescription
namestringThe slot name (must match the name in the dashboard)
optionsUseSlotOptionsOptional. Filter options.

Options:

PropertyTypeDescription
eventstringFilter messages by event name
dismissDurationDaysnumberDays a dismissed message stays hidden for this slot before it can show again. Overrides the provider default. Omit to inherit it (or infinite if neither is set).

Returns:

PropertyTypeDescription
messageSlotMessage | nullThe current message to display. In Stack mode: first non-dismissed message. In A/B Test or Random mode: the selected message. null if no messages available or still loading.
messagesSlotMessage[]All messages for the slot (after mode filtering)
isLoadingbooleantrue during the initial fetch
errorError | nullFetch error, if any
dismiss(messageId?: string) => voidDismiss a message (Stack mode only). Defaults to the current message. Also records a dismiss analytics event.
trackEvent(type: EventType, messageId: string) => voidManually track an event (impression, click, or dismiss)

Filtering by event:

If you need different messages for different contexts within the same slot, pass an event filter:

const { message } = useSlot("notifications", { event: "low-token" })

Dismiss behavior

Calling dismiss() (Stack mode only) hides the message and records a dismiss event. Dismissals are stored in the browser's localStorage, so they are per device/browser — a different device, browser, or cleared storage will show the message again.

By default a dismiss is infinite: once dismissed, a message never reappears. To let a message show again after a delay, set dismissDurationDays:

// Re-show any dismissed message after 30 days, for every slot:
<LoopanaProvider apiKey="pk_live_xxx" dismissDurationDays={30}>

// Override for one slot (e.g. a promo you want back sooner):
useSlot("promo-banner", { dismissDurationDays: 7 })

Precedence: the per-slot useSlot value wins, otherwise the provider default, otherwise infinite. The duration is a developer setting — it is not controlled from the dashboard, so your app's UX stays in your control.


@loopana/js

Browser client for use with any JavaScript framework (Vue, Svelte, vanilla JS, etc.).

LoopanaClient

import { LoopanaClient } from "@loopana/js"

const client = new LoopanaClient({
  apiKey: "pk_live_xxx",
  appOrigin: "https://myapp.com", // optional
})

const messages = await client.fetchAll("en")
const banner = messages.get("homepage-banner")

Constructor options: Same as LoopanaProvider props (see above).

Methods:

MethodDescription
fetchAll(locale?)Fetch all slots and messages. Returns Map<string, SlotData>.
getCache()Returns the last fetched data, or null.
getSlot(name)Get a single slot from the cache.
getSlotMessages(name, filter?)Get filtered messages for a slot (excludes dismissed in Stack mode).
trackEvent(payload)Track an impression, click, or dismiss event.
dismiss(messageId)Dismiss a message and persist to localStorage.
isDismissed(messageId, dismissDurationDays?)Check if a message is currently dismissed (respecting the duration; infinite if omitted).
setUserId(userId)Update the user ID.
getSessionId()Get the current session ID.

@loopana/node

Server-side client for Node.js, Next.js server components, API routes, etc. No browser APIs — works in any Node.js environment.

LoopanaClient

import { LoopanaClient } from "@loopana/node"

const client = new LoopanaClient({ apiKey: "pk_live_xxx" })
const messages = await client.fetchAll("en")

const banner = messages.get("homepage-banner")
if (banner) {
  console.log(banner.messages[0].title)
}

Constructor options:

OptionTypeRequiredDescription
apiKeystringYesProject API key
baseUrlstringNoOverride the API base URL
eventsUrlstringNoOverride the events API base URL
appOriginstringNoYour app's origin for URL resolution
userIdstringNoEnd user ID for targeting
onError(err: unknown) => voidNoCalled when trackEvent fails in the background.

Methods:

MethodDescription
fetchAll(locale?)Fetch all slots and messages. Returns Map<string, SlotData>.
trackEvent(payload)Track an impression, click, or dismiss event.
setUserId(userId)Update the user ID.

Types

SlotMessage

The message object returned by hooks and client methods.

PropertyTypeDescription
idstringUnique message ID
titlestringMessage title (resolved for current locale)
descriptionstring | nullOptional description
urlstring | nullOptional link URL (internal URLs are resolved relative to appOrigin)
actionLabelstring | nullOptional button text
intentstringVisual style: info, success, warning, destructive, or default
eventstring | nullEvent name if the slot uses events
prioritynumberSort priority (higher = first)
isExternalUrlbooleanWhether the URL points outside your app

SlotData

PropertyTypeDescription
messagesSlotMessage[]All messages in the slot
eventsstring[]Available event names
modeSlotMode"stack", "ab_test", or "random"

EventType

type EventType = "impression" | "click" | "dismiss"

Analytics

The SDK automatically tracks impressions when a message is rendered via useSlot. Click and dismiss events are tracked when the user interacts:

  • dismiss() from useSlot records a dismiss event
  • For clicks, call trackEvent("click", messageId)

Analytics data appears in the dashboard under Analytics and on each message's detail page.