useLoyalty
Server SDK

Leaderboards

Surface competitive rankings to drive member engagement

Leaderboards rank members by points earned over a time period. Use them to drive competition, highlight top members, and motivate engagement.

Get Leaderboard

Fetch the ranked list of members.

GET /api/v1/leaderboard

Query Parameters

ParameterTypeDefaultDescription
periodstringall_timeall_time, monthly, or weekly
limitnumber10Results per page (max 100)
offsetnumber0Pagination offset
cursorstringCursor for cursor-based pagination

SDK

// Top 10 all-time
const { data: entries } = await useLoyalty.leaderboards.get({ limit: 10 });

// Top 25 this month
const { data: entries } = await useLoyalty.leaderboards.get({
  period: 'monthly',
  limit: 25,
});

Response

{
  "data": [
    {
      "rank": 1,
      "externalId": "user_456",
      "name": "Sarah K.",
      "avatarUrl": "https://example.com/avatars/sarah.jpg",
      "points": 12500
    },
    {
      "rank": 2,
      "externalId": "user_123",
      "name": "John D.",
      "avatarUrl": null,
      "points": 9800
    }
  ],
  "total": 842,
  "hasMore": true,
  "nextCursor": "cursor_abc123"
}

Get Member's Rank

Fetch the rank and points of a specific member without loading the full leaderboard.

GET /api/v1/leaderboard/rank/:externalId?period=monthly

SDK

const rank = await useLoyalty.leaderboards.getMemberRank('user_123', 'monthly');
// { rank: 14, points: 3200 }

Response

{
  "rank": 14,
  "points": 3200
}

Code Examples

Leaderboard Widget

import { useLoyaltyLeaderboard } from '@useloyalty/sdk/react';

function LeaderboardWidget() {
  const { entries, isLoading } = useLoyaltyLeaderboard({
    fetcher: (opts) =>
      fetch(`/api/leaderboard?period=${opts.period}&limit=${opts.limit}`)
        .then(r => r.json()),
    period: 'monthly',
    limit: 10,
  });

  if (isLoading) return <div>Loading...</div>;

  return (
    <ol>
      {entries.map(entry => (
        <li key={entry.externalId}>
          <span>#{entry.rank}</span>
          <span>{entry.name ?? 'Anonymous'}</span>
          <span>{entry.points.toLocaleString()} pts</span>
        </li>
      ))}
    </ol>
  );
}

Show "Your Rank" Alongside the Leaderboard

async function getLeaderboardWithUserRank(userId: string) {
  const [{ data: top10 }, myRank] = await Promise.all([
    useLoyalty.leaderboards.get({ period: 'monthly', limit: 10 }),
    useLoyalty.leaderboards.getMemberRank(userId, 'monthly'),
  ]);

  return { top10, myRank };
}

Period Tabs

const [period, setPeriod] = useState<'all_time' | 'monthly' | 'weekly'>('monthly');

const { entries, refetch } = useLoyaltyLeaderboard({
  fetcher: (opts) => fetch(`/api/leaderboard?period=${opts.period}`).then(r => r.json()),
  period,
});

// When user switches tab, period changes → fetcher re-runs automatically

Privacy Considerations

Leaderboard entries return externalId, name, and avatarUrl. If your members haven't set a display name, name is null. Consider showing a masked identifier ("Member #1234") instead of the raw externalId in your UI.

function displayName(entry: LeaderboardEntry): string {
  return entry.name ?? `Member #${entry.externalId.slice(-4)}`;
}

On this page