Build
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/sdkStep 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
| Component | Use it for |
|---|---|
| AdQuestWidget | The 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. |
| QuestWidget | A 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 + hooks | Shared SDK state for a custom React interface. Read the headless guide before you implement completion yourself. |
Step 4: Connect application events
| Callback | Host 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. |