Rekomi Docs
For brandsInstall tracking
Brands

Install on Vue

Vite Vue 3 installs the script tag in index.html. Nuxt 3 uses the script array in nuxt.config.ts or useHead. Sales record through your gateway or server-to-server, not a browser convert call.

Vue's install differs between Vite Vue 3 (static index.html) and Nuxt 3 (SSR with no index.html). Pick the path that matches your project. There is no thank-you-page convert snippet to add: sales are recorded off the page, through your payment gateway or a server-to-server call.

Vite Vue 3: install in index.html

In your project's index.html:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Your App</title>
    <!-- Rekomi tracking -->
    <script async src="https://api.rekomi.com/api/v1/r/loader.js" data-program-id="YOUR_PROGRAM_ID"></script>
    <noscript>
      <img src="https://api.rekomi.com/api/v1/r/c.gif" width="1" height="1" alt="" referrerpolicy="no-referrer-when-downgrade" />
    </noscript>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>

Vite serves index.html at dev time and inlines the build output at production. An external <script src> is preserved as-is (only inline non-module scripts get stripped).

Copy the snippet with your program ID already filled in from your campaign's install recipe under Setup > Install in the dashboard.

Nuxt 3: install via nuxt.config.ts

Nuxt 3 is SSR, so there's no static index.html. Configure the head script via the app.head.script array in nuxt.config.ts:

// nuxt.config.ts
export default defineNuxtConfig({
  app: {
    head: {
      script: [
        {
          src: "https://api.rekomi.com/api/v1/r/loader.js",
          async: true,
          "data-program-id": process.env.REKOMI_PROGRAM_ID,
        },
      ],
    },
  },
});

Set REKOMI_PROGRAM_ID in your .env. Nuxt loads it at build time and substitutes it into the rendered head, producing the exact script tag.

Alternative: useHead in app.vue. If you want the script to load conditionally (e.g., only in production), call useHead() inside app.vue instead. Read the program ID from Nuxt's public runtime config. Don't reach for import.meta.env with a NUXT_-prefixed variable: Nuxt only exposes VITE_-prefixed variables there, so the rendered tag would end up with data-program-id="undefined" after hydration.

<script setup>
const config = useRuntimeConfig();
useHead({
  script: [
    {
      src: "https://api.rekomi.com/api/v1/r/loader.js",
      async: true,
      "data-program-id": config.public.rekomiProgramId,
    },
  ],
});
</script>

<template>
  <NuxtPage />
</template>

Declare the public runtime config key in nuxt.config.ts and set NUXT_PUBLIC_REKOMI_PROGRAM_ID in your environment (or hard-code the ID, since it is a public identifier, not a secret):

// nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    public: {
      rekomiProgramId: "", // overridden by NUXT_PUBLIC_REKOMI_PROGRAM_ID
    },
  },
});

The useHead composable is reactive, so if you wrap it in a conditional or computed the script tag updates as state changes. For a static install, the nuxt.config.ts pattern is simpler.

Recording the sale

The browser pixel no longer records sales, so there is no convert call to fire from onMounted. A sale is recorded through one of these paths:

  • Your payment gateway. Connect it once in the Connect payment gateway step at /dashboard/setup/connect-sales (Stripe, Paddle, Braintree, Shopify, Wix, Lemon Squeezy, Chargebee, Polar, Recurly, Gumroad, Creem, or Dodo Payments) and sales, refunds, and cancellations record automatically.
  • Server-to-server. POST the sale from your Nuxt server routes to Rekomi's S2S endpoint with an HMAC signature. See Server-to-server tracking.
  • Coupon codes. Give each affiliate a unique discount code (Campaign > Coupons); a redeemed code credits them at checkout.

Carry the referral into checkout with window.Rekomi.getReferral().

Nuxt server-side checkout via Server Routes

Nuxt 3's server routes are great for Stripe Checkout Session creation. The Rekomi referral lives in the browser; pass it from the client via a custom header and stamp it on the subscription's metadata as rekomi_affiliate_slug server-side (that is what Rekomi reads for attribution):

// server/api/create-checkout-session.post.ts
import Stripe from "stripe";

export default defineEventHandler(async (event) => {
  const referral = getHeader(event, "x-rekomi-referral");
  const body = await readBody(event);

  const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
  const session = await stripe.checkout.sessions.create({
    mode: "subscription",
    line_items: [{ price: body.priceId, quantity: 1 }],
    success_url: `${process.env.APP_URL}/success?session_id={CHECKOUT_SESSION_ID}`,
    cancel_url: `${process.env.APP_URL}/pricing`,
    subscription_data: referral
      ? { metadata: { rekomi_affiliate_slug: referral } }
      : undefined,
  });

  return { url: session.url };
});

From the client (a Vue component):

<script setup>
async function startCheckout(priceId) {
  const referral = window.Rekomi?.getReferral();
  const { url } = await $fetch("/api/create-checkout-session", {
    method: "POST",
    headers: { "x-rekomi-referral": referral || "" },
    body: { priceId },
  });
  window.location.href = url;
}
</script>

Capturing leads (optional)

window.Rekomi.convert() is now a LEAD capture call, not a sale. On a signup page you can tie a customer email to the referral before payment:

<script setup>
import { onMounted } from "vue";

const props = defineProps<{ email: string }>();

onMounted(() => {
  if (props.email) window.Rekomi?.ready?.(() => window.Rekomi.convert(props.email));
});
</script>

<template>
  <div>Thanks for signing up!</div>
</template>

Browser lead capture works on every plan; the server-side lead endpoint requires Starter or higher. See Track leads and signups.

Quirks worth knowing

Nuxt 3 useHead is reactive. Calling useHead inside a setup block makes the head reactive to state changes. For a static install, prefer the nuxt.config.ts approach because it doesn't re-render on every route change.

SSR hydration. Nuxt 3 server-renders pages, then hydrates client-side. window.Rekomi and getReferral() are only available in the browser, so read the referral from client-side code (an onMounted hook or an event handler), never during SSR.

Composition API only. The examples above use Vue 3's Composition API. On Vue 2's Options API, use the mounted() lifecycle hook and this.$route instead of useRoute().

Client navigation keeps the loader. vue-router navigates client-side without a full document reload. The head script loads once on initial visit and getReferral() stays available across route changes.

Troubleshooting

Nuxt 3 script not in rendered head. Confirm REKOMI_PROGRAM_ID is set in .env and your build picked it up. Also check that you're running Nuxt's build, not a stale dev-server HMR cache.

Vite preserves the script tag in dev but not in build. Use an external <script src> (as shown), not an inline block. Vite preserves external script tags and only strips inline non-module scripts.

window.Rekomi.getReferral() returns null. The loader had not captured a referral on this browser: the visitor did not arrive through an affiliate link, or the script is missing on the landing page they hit first. Confirm the tag is present in View Source on every top-of-funnel page.

Referral captured but the sale is not attributed. Forward window.Rekomi.getReferral() into your create-session endpoint and stamp it as subscription_data.metadata.rekomi_affiliate_slug, or include it in your S2S call.