BricqsBricqs

Headless SDK: state and cache

Every hook in the SDK shares the same loading, error, and polling shape. This page covers how the cache works, when to refetch, and how to do optimistic updates without rolling your own state library.

Reading time7 minutes
Last updatedMay 2026

Key takeaways

Quick read
  • All hooks return isLoading, error, and a refetch function. Same shape, every time.
  • The SDK ships a built-in cache keyed by tenant + participant + resource. No external library needed.
  • Polling defaults: 15s for active surfaces (challenge), 30s for ambient (points, tier), 5s during contest final hour.
  • submit and claim auto-invalidate related caches. You rarely call refetch directly.
  • On window.focus, every active hook revalidates. Stale data after a tab switch is rare.

Anatomy

The shape every hook returns

ts
// Every hook returns at minimum:
{
  isLoading: boolean;     // first load only
  isFetching: boolean;    // any background revalidation
  error: Error | null;    // last error, if any
  refetch: () => Promise<void>;
}

// Plus resource-specific data:
{
  available: number;
  lifetime: number;
  // ... etc
}

Polling

Defaults and overrides

Set per-hook

Pass pollInterval={milliseconds}. Use 0 to disable polling for offscreen widgets.

Adapt by context

Inside a contest's final hour, useLeaderboard auto-tightens to 5s. You can mirror this manually with pollInterval={lastHour ? 5000 : 30000}.

Pause when hidden

The Provider listens to document.visibilityState. Hidden tabs stop polling automatically; visible tabs resume.

Manual refetch

Call refetch() after an explicit user action that the SDK could not have observed (e.g. a flow you control end-to-end without an event).

Cache keys

How invalidation works

text
Cache key shape:
  tenant : participant : resource [: subId]

Examples:
  tenant_xyz : p_8a3f : points
  tenant_xyz : p_8a3f : tier
  tenant_xyz : p_8a3f : challenge : onboarding_v3
  tenant_xyz : p_8a3f : leaderboard : april_quiz_cup : bracket

When a mutation lands, it invalidates affected keys:

  submit() in useChallenge   ->   invalidates the same challenge + points + tier
  claim()  in useRewards     ->   invalidates rewards + points
  spendFreeze() in useStreak ->   invalidates the streak + points

You rarely call refetch directly. The SDK keeps state coherent.

Optimistic updates

Render before the server confirms

tsx
const { available, refetch } = usePoints();

async function onPurchaseDone() {
  // Local UI: pretend points have arrived
  optimisticAdd(50);

  // Tell the server
  await emitFromServerSide(/* ... */);

  // SDK auto-revalidates within ~1s, but you can force it:
  await refetch();
}

Most flows do not need optimistic updates. Reserve them for moments where the user just acted and waiting feels broken.

SSR

Hydration without flicker

tsx
// Pass an initial state from the server to skip the loading flash
import { fetchInitialState } from "@bricqs/headless-react/server";

const initial = await fetchInitialState({
  apiKey: process.env.BRICQS_ADMIN_KEY!,
  tenantId: process.env.BRICQS_TENANT!,
  participantId: userId,
});

return (
  <BricqsProvider {...providerProps} initialState={initial}>
    {children}
  </BricqsProvider>
);

initialState seeds the cache. The first render uses real data instead of skeletons; revalidation runs in the background.

Common mistakes

What goes wrong

01Mistake

Wrapping every hook in useEffect to refetch on focus.

Fix

The SDK already does this. Remove the manual code.

02Mistake

Calling refetch in render. Infinite loop.

Fix

refetch is for event handlers and effects with deps, never bare in the render path.

03Mistake

Treating isFetching as isLoading. UI flickers on every poll.

Fix

isLoading is true only on first load. Use it for skeletons. isFetching is for subtle indicators (spinner in the header).

04Mistake

Polling secondary screens at 5 second intervals. Network tab is overwhelmed.

Fix

Default 30 to 60 seconds for ambient widgets. Tighten only for active flows.

Developer FAQ

Common questions when integrating gamification with Bricqs.

Ready to ship?

Wire it up with the Bricqs SDK or API

Headless SDK for React UIs, REST API for any backend. Same engine behind both.

1 brief to align the room2 mechanics max in version one
What happens next
01
Pick the mechanic
Choose the smallest working system for the brief.
02
Launch without rebuilds
Configure rules and rewards in one place.