useLoyalty
Widget

React Integration

Integrate the useLoyalty Widget into React, Next.js, and other React-based frameworks

This guide covers integrating the useLoyalty Widget into React applications using the official @useloyalty/sdk package.

Installation

npm install @useloyalty/sdk
# or
pnpm add @useloyalty/sdk
# or
yarn add @useloyalty/sdk

Quick Start

1. Create Server-Side Auth Endpoint

The SDK provides a helper to generate the HMAC signature:

// app/api/useloyalty/auth/route.ts (Next.js App Router)
import { NextResponse } from 'next/server';
import { generateWidgetAuth } from '@useloyalty/sdk';

export async function POST(request: Request) {
  const { userId } = await request.json();

  if (!userId) {
    return NextResponse.json({ error: 'User ID required' }, { status: 400 });
  }

  const auth = generateWidgetAuth(
    process.env.USELOYALTY_PUBLIC_KEY!,
    process.env.USELOYALTY_PRIVATE_KEY!,
    userId
  );

  return NextResponse.json(auth);
}

2. Add Widget to Your App

// app/layout.tsx
'use client';

import { useLoyaltyWidget } from '@useloyalty/sdk/react';
import { useAuth } from '@/hooks/useAuth';

function UseLoyaltyWidgetLoader() {
  const { user } = useAuth();

  useLoyaltyWidget({
    getAuth: async () => {
      const res = await fetch('/api/useloyalty/auth', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ userId: user?.id }),
      });
      return res.json();
    },
    member: {
      email: user?.email,
      name: user?.name,
    },
    theme: {
      primaryColor: '#6366f1',
    },
  });

  return null;
}

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        {children}
        <UseLoyaltyWidgetLoader />
      </body>
    </html>
  );
}

Option B: Using Provider Pattern

// app/providers.tsx
'use client';

import { UseLoyaltyProvider } from '@useloyalty/sdk/react';
import { useAuth } from '@/hooks/useAuth';

export function Providers({ children }) {
  const { user } = useAuth();

  return (
    <UseLoyaltyProvider
      getAuth={async () => {
        const res = await fetch('/api/useloyalty/auth', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ userId: user?.id }),
        });
        return res.json();
      }}
      theme={{ primaryColor: '#6366f1' }}
    >
      {children}
    </UseLoyaltyProvider>
  );
}

Then use anywhere in your app:

// components/RewardsButton.tsx
'use client';

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

export function RewardsButton() {
  const { isReady, open } = useLoyalty();

  return (
    <button onClick={open} disabled={!isReady}>
      🎁 View Rewards
    </button>
  );
}

Server-Side SDK

Generate Widget Auth

import { generateWidgetAuth, generateWidgetAuthWithMember } from '@useloyalty/sdk';

// Basic auth
const auth = generateWidgetAuth(
  process.env.USELOYALTY_PUBLIC_KEY!,
  process.env.USELOYALTY_PRIVATE_KEY!,
  userId
);

// With member data
const authWithMember = generateWidgetAuthWithMember(
  process.env.USELOYALTY_PUBLIC_KEY!,
  process.env.USELOYALTY_PRIVATE_KEY!,
  userId,
  {
    email: user.email,
    name: user.name,
    avatarUrl: user.avatar,
  }
);

API Client

Use the typed client for server-side API calls:

import { UseLoyaltyClient } from '@useloyalty/sdk';

const useLoyalty = new UseLoyaltyClient({
  publicKey: process.env.USELOYALTY_PUBLIC_KEY!,
  privateKey: process.env.USELOYALTY_PRIVATE_KEY!,
});

// Award points
await useLoyalty.members.awardPoints(userId, {
  amount: 100,
  description: 'Welcome bonus',
});

// Track events
await useLoyalty.events.track(userId, {
  event: 'purchase',
  properties: { amount: 99.99 },
});

// Complete a quest
await useLoyalty.quests.complete(userId, 'quest_123');

// Award a badge
await useLoyalty.badges.award(userId, 'badge_early_adopter');

// Redeem promo code
await useLoyalty.promoCodes.redeem(userId, { code: 'WELCOME50' });

Custom Trigger Button

Use displayMode: 'custom' to hide the floating button and trigger from your own UI:

'use client';

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

export function App() {
  const { isReady, open, toggle } = useLoyaltyWidget({
    getAuth: async () => {
      const res = await fetch('/api/useloyalty/auth', { method: 'POST' });
      return res.json();
    },
    theme: {
      launcher: {
        displayMode: 'custom', // Hide floating button
      },
    },
  });

  return (
    <nav>
      <button onClick={open} disabled={!isReady}>
        🎁 Rewards
      </button>
    </nav>
  );
}

With UseLoyaltyTrigger Component

import { UseLoyaltyProvider, UseLoyaltyTrigger } from '@useloyalty/sdk/react';

function App() {
  return (
    <UseLoyaltyProvider
      getAuth={fetchAuth}
      theme={{ launcher: { displayMode: 'custom' } }}
    >
      <nav>
        <UseLoyaltyTrigger className="nav-button">
          🎁 Rewards
        </UseLoyaltyTrigger>
      </nav>
    </UseLoyaltyProvider>
  );
}

React Hooks Reference

useLoyaltyWidget

Main hook for widget management:

const {
  isLoaded,   // Script loaded
  isReady,    // Widget initialized
  isOpen,     // Widget drawer open
  error,      // Any error
  open,       // Open widget
  close,      // Close widget
  toggle,     // Toggle widget
  init,       // Manual init
  destroy,    // Cleanup
} = useLoyaltyWidget({
  getAuth,           // Required: async function returning WidgetAuth
  member,            // Optional: member data
  theme,             // Optional: widget theme
  language,          // Optional: widget language
  scriptUrl,         // Optional: custom script URL
  apiUrl,            // Optional: custom API URL
  onReady,           // Optional: ready callback
  onError,           // Optional: error callback
  onWelcomeBonus,    // Optional: welcome bonus callback
  autoInit,          // Optional: auto-init (default: true)
});

useLoyalty

Context hook (use within UseLoyaltyProvider):

const { isReady, open, close, toggle } = useLoyalty();

useLoyaltyAuth

Helper hook for fetching auth:

const { auth, isLoading, error, refetch } = useLoyaltyAuth({
  endpoint: '/api/useloyalty/auth',
  userId: user?.id ?? null,
  member: { email: user?.email },
});

TypeScript Types

The SDK exports all necessary types:

import type {
  // Widget
  WidgetAuth,
  WidgetTheme,
  WidgetConfig,
  WidgetLanguage,
  LauncherConfig,
  LauncherDisplayMode,
  LauncherVariant,
  LauncherIcon,
  MemberData,

  // API
  Member,
  AwardPointsInput,
  TrackEventInput,
  Badge,
  Quest,
} from '@useloyalty/sdk';

import type {
  UseLoyaltyWidgetOptions,
  UseLoyaltyWidgetReturn,
} from '@useloyalty/sdk/react';

Next.js App Router Example

Complete example with authentication:

// app/api/useloyalty/auth/route.ts
import { NextResponse } from 'next/server';
import { generateWidgetAuthWithMember } from '@useloyalty/sdk';
import { getServerSession } from 'next-auth';

export async function POST() {
  const session = await getServerSession();

  if (!session?.user) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
  }

  const auth = generateWidgetAuthWithMember(
    process.env.USELOYALTY_PUBLIC_KEY!,
    process.env.USELOYALTY_PRIVATE_KEY!,
    session.user.id,
    {
      email: session.user.email,
      name: session.user.name,
      avatarUrl: session.user.image,
    }
  );

  return NextResponse.json(auth);
}
// app/layout.tsx
import { UseLoyaltyWidgetClient } from '@/components/UseLoyaltyWidgetClient';
import { getServerSession } from 'next-auth';

export default async function RootLayout({ children }) {
  const session = await getServerSession();

  return (
    <html>
      <body>
        {children}
        {session?.user && <UseLoyaltyWidgetClient />}
      </body>
    </html>
  );
}
// components/UseLoyaltyWidgetClient.tsx
'use client';

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

export function UseLoyaltyWidgetClient() {
  useLoyaltyWidget({
    getAuth: async () => {
      const res = await fetch('/api/useloyalty/auth', { method: 'POST' });
      if (!res.ok) throw new Error('Auth failed');
      return res.json();
    },
    theme: {
      primaryColor: '#6366f1',
      launcher: {
        variant: 'icon-text',
        text: 'Rewards',
        pulse: true,
      },
    },
    onReady: () => console.log('Widget ready'),
  });

  return null;
}

Troubleshooting

Widget Not Appearing

  1. Check that the user is authenticated
  2. Verify API route returns valid auth data
  3. Check browser console for errors

Hydration Errors

Ensure you use 'use client' directive:

'use client';

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

Multiple Widget Instances

The hook handles this automatically with a ref check. Only one widget instance is created.

Cleanup on Route Changes

The hook automatically cleans up on unmount. For manual cleanup:

const { destroy } = useLoyaltyWidget({ ... });

// Call when needed
destroy();

On this page