BricqsBricqs

Headless SDK: streaks

useStreak returns the live streak count, freeze inventory, grace status, and the next milestone. Wire one tick event a day; the SDK renders the rest.

Last updatedMay 2026

Key takeaways

Quick read
  • useStreak(code) returns a StreakStatus: currentCount, longestCount, freezesAvailable, isAtRisk.
  • Tick the streak server-side. Sending the event from the client risks losing days when the user is offline.
  • Freezes are grace periods, spent automatically on a miss while any remain — there is no manual spend call.
  • Read freezesAvailable to show the safety net; surface isAtRisk to nudge the user before the period ends.
  • Always render the freeze count. Hidden inventory undermines the safety net.

Render the streak

One hook, full state

components/StreakWidget.tsx·tsx
"use client";
import { useStreak } from "@bricqs/sdk-react";

export function StreakWidget() {
  // participantId inherited from <BricqsProvider> / participantToken
  const { streak, isLoading } = useStreak("daily_practice");

  if (isLoading || !streak) return null;

  return (
    <section className="rounded-xl border p-5">
      <header className="flex items-baseline justify-between mb-2">
        <h3 className="font-bold text-2xl">{streak.currentCount} day streak</h3>
        {streak.freezesAvailable > 0 && (
          <span className="text-sm text-slate-500">
            {streak.freezesAvailable} freeze{streak.freezesAvailable === 1 ? "" : "s"} left
          </span>
        )}
      </header>
      {streak.isAtRisk && (
        <p className="text-amber-600 text-sm">
          You haven&apos;t recorded today yet — record before the period ends to keep the
          streak. If you miss, a freeze is spent automatically (while any remain).
        </p>
      )}
      <p className="text-sm text-slate-500 mt-3">Longest: {streak.longestCount} days</p>
    </section>
  );
}

Tick the streak

One event a day, server-side

Always tick from the server. Client-side ticks lose days when the user is offline.

server-side: when the user does the qualifying action·ts
import { emitToBricqs } from "@/lib/bricqs";

export async function logPractice(userId: string) {
  await savePractice(userId);

  // One tick per day. Idempotent: same key means same day.
  const today = new Date().toISOString().slice(0, 10); // YYYY-MM-DD
  await emitToBricqs(
    userId,
    "streak_tick",
    { streak_id: "daily_practice" },
    `p_${userId}:streak_tick:daily_practice:${today}`
  );
}

Milestones

Celebrate the long runs

tsx
// Derive celebration moments from the streak count. Poll-based today
// (refreshInterval), so compare against the previous render.
const { streak } = useStreak("daily_practice");
const prev = usePrevious(streak?.currentCount ?? 0);

useEffect(() => {
  const n = streak?.currentCount ?? 0;
  if ([7, 30, 100].includes(n) && n !== prev) {
    showCelebration(`${n}-day streak!`);
    fireConfetti();
  }
}, [streak?.currentCount, prev]);

Common mistakes

What breaks streaks

01Mistake

Ticking from the client. Offline users lose days; the streak appears broken.

Fix

Always tick from the server when the qualifying action lands. Use idempotent keys so retries are safe.

02Mistake

Hiding the freeze count. The safety net feels punitive when invisible.

Fix

Always show freezesAvailable. The user only uses the freeze if they know it exists.

03Mistake

Allowing infinite freeze accumulation. The streak loses meaning.

Fix

Cap freezes server-side (configured at the streak definition). The SDK reflects the cap; do not implement client-side caps.

04Mistake

Showing 'You broke your streak!' immediately on miss. Users uninstall in anger.

Fix

Read isAtRisk and nudge the user to record. A freeze is spent automatically while any remain; only treat it as broken once currentCount resets.

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.