Skip to documentation
AdQuestDocs

React & Next.js

Add the ready-made quest and rewards widget to a React 18+ application.

On this page

Prerequisites

  • React and React DOM 18 or newer.
  • Your public API key and API URL.
  • A backend route that creates participant sessions (see Participant sessions).

Step 1: Install the SDK

Terminal
npm install @adquest/sdk

Step 2: Mount the widget with a participant session

Rewards.tsx
"use client";
import { useCallback, useEffect, useState } from "react";
import { AdQuestWidget } from "@adquest/sdk/react";
import "@adquest/sdk/styles.css";

type Session = { externalUserId: string; participantToken: string; expiresAt: string };

// "en" or "fa". Use the same value when your backend creates the participant session.
Highlighted: const LOCALE = "en-US";

export default function Rewards() {
  const [session, setSession] = useState<Session | null>(null);
  const [error, setError] = useState("");
  const [loading, setLoading] = useState(false);
  const refresh = useCallback(async () => {
    setLoading(true);
    setError("");
    try {
      const response = await fetch("/api/adquest/session", {
        method: "POST",
        cache: "no-store",
        headers: { "Content-Type": "application/json" },
Highlighted:         body: JSON.stringify({ locale: LOCALE }),
      });
      if (!response.ok) throw new Error("Sign in or retry to load your rewards.");
      setSession(await response.json());
    } catch (error) {
      setSession(null);
      setError(error instanceof Error ? error.message : "Could not load rewards.");
    } finally {
      setLoading(false);
    }
  }, []);

  useEffect(() => { void refresh(); }, [refresh]);
  useEffect(() => {
    if (!session) return;
    const delay = Math.max(1000, Date.parse(session.expiresAt) - Date.now() - 60000);
    const timer = window.setTimeout(() => void refresh(), delay);
    return () => window.clearTimeout(timer);
  }, [session, refresh]);

  if (error) return <div role="alert">{error} <button disabled={loading} onClick={refresh}>Retry</button></div>;
  if (!session) return <p role="status">Loading rewards…</p>;
  return <AdQuestWidget
    key={session.externalUserId}
    apiKey={process.env.NEXT_PUBLIC_ADQUEST_PLATFORM_API_KEY!}
    baseUrl={process.env.NEXT_PUBLIC_ADQUEST_API_URL!}
    externalUserId={session.externalUserId}
    participantToken={session.participantToken}
Highlighted:     locale={LOCALE}
    onAuthRequired={() => void refresh()}
    onError={(error) => setError(error.message)}
  />;
}

Choose the language

Pass locale to AdQuestWidget. Use the same value when your backend creates the participant session.

Widget language
<AdQuestWidget
  apiKey={apiKey}
  baseUrl={baseUrl}
  externalUserId={session.externalUserId}
  participantToken={session.participantToken}
Highlighted:   locale="en-US" // "en" or "fa"
/>

Step 3: Choose the experience

ComponentUse it for
AdQuestWidgetThe lottery, token balance, entries, claims, and earn-token journey. It is lottery-first. Set theme.layoutPreset to quests_rewards for the Quests and Rewards layout.
QuestWidgetA quest-only placement. Pass apiKey, baseUrl, externalUserId, and participantToken. It does not expose the same auth and error callbacks. Manage session expiry in your host.
AdQuestProvider + hooksShared SDK state for a custom React interface. Read the headless guide before you implement completion yourself.

Step 4: Connect application events

CallbackHost action
onReady()Hide your widget loading indicator.
onComplete(result)Show completion feedback and refresh your server-backed state. tokensAwarded is the participant share.
onEntry(purchase)Show entry confirmation.
onClaim(claim)Show claim submission feedback.
onNavigate({ view, canGoBack })Keep host navigation in sync.
onAuthRequired()Refresh the participant token through your backend.
onError({ code, message })Show a recoverable error or account action.
onClose()Close the containing drawer, screen or modal.

Next steps

Something unclear, missing or wrong here? Tell us