## Claimr widget integration
Claimr is embedded with one script, `https://widgets.claimr.io/claimr.min.js`. The script renders the
campaign in an iframe inside a container element and exposes an SDK at `window.claimr`.
There is no npm package.
### Project values
- Organization ID: <your-organization-id>
- Campaign ID: <your-campaign-id>
- Container ID: claimr-widget
- Sign-in model: <widget sign-in | user token | wallet (platform "dapp") | telegram>
- Framework: <React + Vite | Next.js App Router | Next.js Pages Router | Vue 3 + Vite | Nuxt>
### Reference docs (Markdown, read before writing code)
- Rules and shared loader module: https://docs.claimr.io/frameworks/overview.md
- React: https://docs.claimr.io/frameworks/react.md
- Next.js: https://docs.claimr.io/frameworks/nextjs.md
- Vue and Nuxt: https://docs.claimr.io/frameworks/vue-and-nuxt.md
- SDK methods and callbacks: https://docs.claimr.io/sdk-guide.md
- Script attributes: https://docs.claimr.io/widget/widget-attributes.md
- User tokens: https://docs.claimr.io/api-guide/user-token.md
- Full index: https://docs.claimr.io/llms.txt
### Rules
1. Use the shared loader module (`lib/claimr.ts`) from the overview page. Do not write a new loader.
2. Load the script exactly once per page session, from the app root: root provider (React), root layout
(Next.js App Router), `_app` (Pages Router), app plugin (Vue), or `plugins/*.client.ts` (Nuxt).
Never load it from the component that displays the widget.
3. The script element must have `id="claimr-script"`. There must be only one on the page.
4. Configure the widget with `data-*` attributes on the script: `data-organization`, `data-campaign`,
`data-container`, `data-autoresize="true"`. They are read once at load. For runtime changes use SDK
methods (`set_language`, `set_theme`, `set_user_token`), or `reloadClaimr(config)` to switch campaigns.
5. Show the widget by rendering an empty element whose `id` equals `data-container`. The loader attaches
the iframe when that element appears and removes it when it disappears. Never render children inside
it and never bind an inline `height` to it.
6. Never call `window.claimr.destroy()` when a component unmounts.
7. Read `window.claimr` at call time. Never store it in state, context, or a module variable.
8. Assign SDK callbacks (`on_user_info`, `on_campaign_info`, `on_logout`, `on_wallet_request`, and so on)
in exactly one place, the `setup` function passed to `loadClaimr`, and share the data through app state.
9. `set_language`, `set_theme`, `complete_task`, `select_wallet`, `open_profile_popup` and `platform_login`
need a loaded widget: wrap them in `withWidget(...)`. `set_user_token`, `connect_wallet`, `open_quest`
and `login` are safe to call earlier.
10. Run Claimr code in the browser only. Next.js: `'use client'` and `useEffect`. Nuxt: `.client` plugin
and `onNuxtReady`. Rendering the empty container on the server is fine.
11. The Claimr API token is a server secret. Never expose it with `VITE_`, `NEXT_PUBLIC_`, or
`NUXT_PUBLIC_`. Generate user tokens on the server:
`GET https://prod.claimr.io/api/v1/token?account=<user id>&platform=<platform name, not "claimr">`
with `Authorization: Bearer <API token>`. The response is `{ "success": true, "data": { "token": "..." } }`.
Pass the token to the browser, then to `set_user_token`. Call `window.claimr.logout()` on sign-out.
12. When the widget component unmounts, reset `document.body.style.overflow = ''`.
13. To disable autoresize, omit `data-autoresize`. The value `"false"` still enables it.
14. Only use SDK methods, callbacks, and attributes listed in the SDK guide and widget attributes pages.
If something is not documented, ask instead of guessing.
### Definition of done
- The widget renders on the target page and `window.claimr.is_claimr_ready` is `true`.
- Navigating away and back re-attaches the widget, and `document.querySelectorAll('#claimr-script').length === 1`.
- No server-side rendering errors and no hydration warnings.
- The API token does not appear anywhere in the client bundle.