Skip to main content
This guide covers two setups:
  • Vue 3 with Vite: a client-rendered app, usually with Vue Router.
  • Nuxt 3 or 4: server-rendered, with a client-only plugin and a Nitro server route for user tokens.
Both use the same pattern: a plugin loads the script once and turns SDK callbacks into reactive state, a ClaimrWidget component renders the container, and a useClaimr() composable gives components access to state and methods.
Read How the loader works for the reasoning behind this layout. In short: the script loads once for the whole app, and the widget attaches to whichever ClaimrWidget is currently mounted.

Vue 3 with Vite

1

Add environment variables

.env
2

Add the shared loader module

Create src/lib/claimr.ts with the contents of the shared loader module.
3

Create the plugin

src/plugins/claimr.ts
4

Create the composable

src/composables/useClaimr.ts
5

Create the widget component

src/components/ClaimrWidget.vue
Render at most one ClaimrWidget at a time, and do not wrap it in <KeepAlive>. A kept-alive component is moved out of the document when it is deactivated, so the loader detaches the widget and reloads it on every reactivation.
6

Install the plugin

src/main.ts
Pass options to override the defaults, for example createClaimr({ language: 'fr', platform: 'dapp' }).
7

Render the widget

src/views/QuestsView.vue
With Vue Router, other views do not render the container, so the loader detaches the widget when you leave this route and attaches it when you come back.

Use the SDK from components

src/components/LessonVideo.vue
complete_task only works for tasks configured as front-end tasks in the campaign. See SDK tasks.

Sign users in with your own accounts

Generate the token on your backend with your secret API token. Never call the token endpoint from Vue code.
server/claimr-token.ts
Expose it through an authenticated endpoint of your own, then pass the token to the composable whenever your app’s user changes:
src/App.vue
Enable the matching sign-in option in the campaign under Settings > Sign-in options. See User token.
Using Pinia? You can keep ClaimrState in a Pinia store instead of provide/inject. Keep the rule the same: the plugin that loads the script is the only code that assigns SDK callbacks, and it writes into the store.

Nuxt

This setup works with Nuxt 3 and Nuxt 4. Paths are relative to your source directory: the project root in Nuxt 3, or app/ in Nuxt 4. Server files always live in server/ at the project root.
1

Add runtime config

nuxt.config.ts
.env
2

Add the shared loader module

Create lib/claimr.ts with the contents of the shared loader module. It guards every window access, so importing it during server rendering is safe.
3

Create the composable

useState gives you state that is shared across components and safe to use during server rendering.
composables/useClaimr.ts
4

Create the client plugin

The .client suffix keeps the plugin out of server rendering. onNuxtReady delays loading until hydration is finished, so the loader never adds its iframe to server-rendered markup that Vue is still hydrating.
plugins/claimr.client.ts
5

Create the widget component

components/ClaimrWidget.vue
There is no need for <ClientOnly>. The empty <div> renders on the server, and the widget attaches to it in the browser.
6

Render the widget on a page

pages/quests.vue
Navigating with <NuxtLink> to a page without ClaimrWidget detaches the widget. Coming back attaches it again.

Sign users in with your own accounts (Nuxt)

1

Create the server route

The route reads your secret API token from server-only runtime config. This example uses nuxt-auth-utils; replace requireUserSession with your auth library’s equivalent.
server/api/claimr/token.get.ts
The token never expires. Store it with your user record to avoid calling Claimr on every request.
2

Pass the token to the widget

plugins/claimr-auth.client.ts
Enable the matching sign-in option in the campaign under Settings > Sign-in options.

Wallets and Telegram

  • dApp with its own wallet: pass platform: 'dapp' in the loader config and assign window.claimr.on_wallet_request once your wallet library is ready. The handler receives { op, ...params } for connect, sign_message, send_transaction, and disconnect, and must return a result or throw. See on_wallet_request and the React wallet bridge, which translates directly to a Vue component using @wagmi/vue.
  • Telegram Mini App: pass platform: 'telegram'. See Telegram Mini App.

Verify the integration

  1. Open the page with ClaimrWidget. The campaign renders inside the container.
  2. In the browser console, window.claimr.is_claimr_ready returns true.
  3. Navigate to another route and back. The widget comes back, and document.querySelectorAll('#claimr-script').length is still 1.
  4. In Nuxt, the server log and browser console show no hydration mismatch warnings for the widget container.
  5. If you use user tokens, sign in to your app and confirm the widget shows the same user without a second sign-in.