React SDK
React hooks for amba — useAmba, useUser, useCollection, useFlag, useEntitlement, useVariant, useTrackOnMount. Companion to @layers/amba-web.
@layers/amba-react wraps @layers/amba-web in reactive hooks that re-render when SDK state changes. Same imperative surface is still available through useAmba() for one-off calls — the hooks just add the missing reactivity layer.
Works with React 18 and React 19 (server components are not supported — every hook is a client hook).
1. Install
react is a peer dependency; the package will use whichever React version your app has installed. @layers/amba-web is the underlying SDK — the hooks are a thin reactive layer over it.
2. Configure before render
Configure the underlying @layers/amba-web SDK before rendering <AmbaProvider>. The provider does not configure the SDK itself — it just plugs the React tree into the SDK's state-change channel.
For Next.js, do the configure call inside a client-side bootstrap component that mounts at the root and wraps children in <AmbaProvider>. Use NEXT_PUBLIC_AMBA_PROJECT_ID and NEXT_PUBLIC_AMBA_CLIENT_KEY in your .env.local.
3. First hook: useUser
4. First collection: useCollection
useCollection re-fetches on mount, on options change, and whenever the SDK auth state changes. response.next_cursor is exposed on the returned response for cursor pagination.
5. Flags and entitlements
useEntitlement defaults to false while loading — safe for paywall gating (no flash-of-pro-content).
6. Track on mount
For page-view tracking:
The track call is fire-and-forget; failures are swallowed so a flaky network doesn't crash render.
All hooks at a glance
@layers/amba-react exports the hooks below. A resource hook returns { data, loading, error, refetch } plus the helpers listed.
Every hook calls the matching @layers/amba-web method, so the SDK reference for each namespace applies; the Notes column lists the hooks whose arguments differ from the SDK call. useDeepLinks().create posts to a client route the API does not have, so it fails; create deep links with the admin API or amba_tracked_links_create.
Core
| Hook | Returns | Notes |
|---|---|---|
useAmba() | Raw Amba SDK handle | Re-renders on auth state change. |
useAuth() | { user, isAuthenticated, isLoading, error, signInAnonymously, signInWithEmail, signUpWithEmail, signInWithApple, signInWithGoogle, requestEmailOtp, verifyEmailOtp, requestSmsOtp, verifySmsOtp, requestMagicLink, verifyMagicLink, linkAccount, signOut, refresh } | Single entry point for the auth surface. |
useUser() | { user, isAuthenticated, loading, error, update } | Fetches auth.me() when authenticated. |
useEvents() | { track } | Stable callback that fires a tracked event. |
useCollection(name, options) | { data, loading, error, refetch, response, insert, update, delete } | Re-fetches on options JSON change + auth change. |
useFlag(name) | FlagAssignment | null | Read .enabled for the boolean gate. null while loading or when unassigned. |
useFlags() | { data, loading, error, refetch } | Bulk flag fetch; useFlag reads from it. |
useVariant(name) | string | null | Multi-variant flag assignment. |
useEntitlement(name) | boolean | Defaults to false while loading. |
useEntitlements() | { data, loading, error, refetch } | Full list of entitlement rows. |
useConfig() | { data, loading, error, refetch } | data is the remote-config bundle ({ version, values }). |
usePush() | { tokens, loading, error, refetch, register, unregister, subscribe, unsubscribe } | Push-token + topic-subscription helpers. |
useMedia() | { presign, upload, list, delete, download } | presign / upload take the storage params object; delete / download take an asset id. |
useTrackOnMount(event, props) | side-effect — no return | Fires once on mount; never throws. |
Social
| Hook | Returns | Notes |
|---|---|---|
useFriends() | { data, loading, error, refetch, sendRequest, acceptRequest, declineRequest, removeFriend, blockUser, unblockUser } | |
useGroups() | { data, loading, error, refetch, create, get, update, delete, members, join, leave, invite } | No list fetch; data stays null. create takes a GroupCreate. |
useMessaging() | { data, loading, error, refetch, createConversation, listMessages, sendMessage, markRead } | data holds messaging.conversations(). listMessages(id, { limit, offset }). |
useFeeds(feed?) | { data, loading, error, refetch } | Activity feed; feed defaults to "default". |
useReviews(targetType?, targetId?) | { data, loading, error, refetch, create, update, delete } | data is reviews.list(targetType, targetId); no fetch until both are set. |
Lifecycle
| Hook | Returns | Notes |
|---|---|---|
useContent(libraryKey?) | { data, loading, error, refetch, item } | Without a key, data is content.today(). |
useOnboarding() | { status, loading, error, refetch, nextStep, skipStep, complete } | nextStep(payload?) sends payload (default {}). |
useDeepLinks() | { get, create } |
Gamification and economy
| Hook | Returns | Notes |
|---|---|---|
useAchievements() | { data, loading, error, refetch, progress } | data is achievements.getAll(); progress() is getProgress(). |
useXp() | { data, loading, error, refetch, history, claim } | data is xp.getBalance(); history(limit?). |
useStreaks() | { data, loading, error, refetch, qualify } | data is streaks.getAll(). |
useLeaderboards(key?) | { data, loading, error, refetch, entries, myRank } | Fetches leaderboards.get(key) with a key. |
useLeagues() | { data, loading, error, refetch, cohort } | data is leagues.me(). |
useChallenges() | { data, loading, error, refetch, get, progress, claim } | |
useCurrencies(currencyCode?) | { data, loading, error, refetch, transactions } | data is currencies.getBalance(), narrowed to one currency when a code is given. |
useInventory() | { data, loading, error, refetch, item, purchase, consume } | consume(itemId, quantity = 1); purchase resolves with the receipt. |
useStore(key?) | { data, loading, error, refetch, purchase } | Without a key, data lists the stores. |
Escape hatch: raw SDK
When you need something the hooks don't cover yet (e.g. storage.upload, ai.anthropic.messages.create, push.register), use useAmba():
useAmba() is just Amba from @layers/amba-web plus subscription to the version counter — so the component re-renders when the SDK's auth state changes.
Server components
Server components run before any client SDK is configured. Don't import @layers/amba-react (or @layers/amba-web) from server-only modules.
Common pitfalls
- Hook called outside
<AmbaProvider>throws"hooks must be used inside <AmbaProvider>". Wrap the root. Amba.configure()not awaited before render causes hooks to throw"amba SDK not configured". Eitherawaitit before the first render, or render a<SDKLoading />boundary that flips whenAmba.appUserIdis no longerundefined.- Two copies of
@layers/amba-web—useUser,useFlag, etc. all import the same singleton from@layers/amba-web. Ifpnpm why @layers/amba-webshows two copies, the hooks subscribe to one and yourAmba.configure()call configures the other. Hoist withpnpm dedupe.
See also
- Web SDK — the underlying imperative surface.
- Client API reference — full method signatures available through
useAmba(). - Code samples — same operations side-by-side with the other 7 SDKs.