# React & Next.js

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

Source: https://dashboard.adquest.batriders.ir/docs/react · Audience: Developers · Section: Build

## 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).

## 1. Install the SDK

**Terminal**

```bash
npm install @adquest/sdk
```

> **Where it runs.** Use React and React DOM 18 or newer. In Next.js, render the widget in a client component and import @adquest/sdk/styles.css once in your layout. Your API base URL must include /api/v1.

## 2. Mount the widget with a participant session

**Rewards.tsx**

```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.
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" },
        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}
    locale={LOCALE}
    onAuthRequired={() => void refresh()}
    onError={(error) => setError(error.message)}
  />;
}
```

> **Connect your existing authentication.** Mount this component only for the current signed-in user. The session route should return the camelCase object from the participant-session helper. Set the public API key at build time in Next.js. Unmount the widget on logout or account switch. If your backend uses CSRF tokens, send that token with the session request.

## Choose who provides the header

By default the widget shows its own header. If your app already has one, set hostNavigation on AdQuestWidget. The widget hides its header and shows a Back to platform button on the home page. Inner pages keep their own Back button. Close or leave the screen in onExit.

**Provide your own header**

```tsx
import { AdQuestWidget } from "@adquest/sdk/react";

export function Rewards({ session, onLeave }: { session: { externalUserId: string; participantToken: string }; onLeave: () => void }) {
  return (
    <AdQuestWidget
      apiKey={process.env.NEXT_PUBLIC_ADQUEST_API_KEY!}
      baseUrl="https://api.adquest.batriders.ir/api/v1"
      {...session}
      hostNavigation
      onExit={onLeave}
    />
  );
}
```

## Choose the language

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

**Widget language**

```tsx
<AdQuestWidget
  apiKey={apiKey}
  baseUrl={baseUrl}
  externalUserId={session.externalUserId}
  participantToken={session.participantToken}
  locale="en-US" // "en" or "fa"
/>
```

- [Language and country](https://dashboard.adquest.batriders.ir/docs/participant-sessions#language): Supported values, what the locale controls and how to change it.

## 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. |

## 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. |

> **Callbacks are not rewards.** An AdQuestWidget ref exposes navigate(view), getView(), and goBack(). goBack() returns false at the root, where the host can close the experience. Use verified server results or signed webhooks before you grant a durable reward in your own app.

## Next steps

- [Tokens, lotteries & claims](https://dashboard.adquest.batriders.ir/docs/rewards): Read balances and enter lotteries.
- [Theme overrides](https://dashboard.adquest.batriders.ir/docs/sdk/react#theme): Match the widget to your app in code.
- [Test & go live](https://dashboard.adquest.batriders.ir/docs/launch): Check the full journey before launch.
