RekomiRekomiBlogPricing
Rekomi Docs
Rekomi Docs
Welcome to Rekomi
Quickstart for brandsPlans and trialsIntegrationsStripe Connect (sales tracking)Organization settingsTeam managementNotifications
Install tracking

Payment platforms

Install on StripeStripe Marketplace appInstall on PaddleInstall on BraintreeInstall on ShopifyInstall on Lemon SqueezyInstall on ChargebeeInstall on PolarInstall on RecurlyInstall on GumroadInstall on CreemInstall on Dodo PaymentsS2S API (any gateway)

Membership and course platforms

Install on MemberstackInstall on OutsetaInstall on PodiaInstall on TeachableInstall on Thinkific

Newsletter and creator platforms

Install on GhostInstall on beehiivInstall on Kit (ConvertKit)

Site builders

Install on WordPressInstall on WebflowInstall on FramerInstall on SquarespaceInstall on WixInstall on BubbleInstall on Carrd

Frameworks

Install on Vanilla JavaScriptInstall on Next.js App RouterInstall on Next.js Pages RouterInstall on React (Vite, CRA, Remix)Install on VueInstall on RailsInstall on Django

Campaigns and commissions

CampaignsCommission modelsPay per click or lead (CPC & CPL)Coupon-code attributionSub-affiliate recruitingTracking and attribution

Affiliates

Recruit affiliatesManage affiliatesAI co-pilotApply to the curated network

Money flow

SalesPayoutsMulti-currencyTax formsReports

Email

Sending domainBroadcasts
For brandsInstall tracking
|Brands|

Install on Vue

Vite Vue 3 installs in index.html. Nuxt 3 uses useHead in app.vue or the script array in nuxt.config.ts. Convert fires from onMounted on the success page.

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, then fire the convert event from onMounted() on your post-purchase success page using useRoute() from vue-router.

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>
    <script>
      (function(){
        var s=document.createElement('script');
        s.src='https://js.rekomi.com/v1/rekomi.js';
        s.async=true;
        s.dataset.programId='YOUR_PROGRAM_ID';
        document.head.appendChild(s);
      })();
    </script>
  </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. The IIFE wrapper makes Vite preserve the inline script (Vite strips non-module scripts that aren't IIFE-wrapped).

Your program ID is in Rekomi at Settings > Tracking.

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://js.rekomi.com/v1/rekomi.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 into the rendered head.

Alternative: useHead in app.vue. If you want the script to load conditionally (e.g., only in production), you can call useHead() inside app.vue instead:

<script setup>
useHead({
  script: [
    {
      src: "https://js.rekomi.com/v1/rekomi.js",
      async: true,
      "data-program-id": import.meta.env.NUXT_REKOMI_PROGRAM_ID,
    },
  ],
});
</script>

<template>
  <NuxtPage />
</template>

The useHead composable is reactive — 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.

Fire convert from onMounted

On your post-purchase success page (typically pages/success.vue):

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

const route = useRoute();

onMounted(() => {
  const sessionId = route.query.session_id;
  if (!sessionId) return;

  const amount = parseInt(route.query.amount || "0", 10);
  const email = route.query.email || "";

  if (window.Rekomi) {
    window.Rekomi.convert({
      external_event_id: sessionId,
      amount_cents: amount,
      currency: "USD",
      customer_email: email,
    });
  }
});
</script>

<template>
  <div>Thanks for your purchase!</div>
</template>

onMounted runs after the component renders and after the script has had a chance to hydrate. The if (window.Rekomi) guard handles edge cases where the loader hasn't finished on slow networks.

For production, prefer fetching canonical order data from your backend (e.g., a Nuxt server route at server/api/checkout-session/[id].ts) rather than trusting URL params.

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 cookie; pass it via a custom header from the client:

// 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`,
    client_reference_id: referral || undefined,
    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>

Quirks worth knowing

Vite Vue 3 needs IIFE wrapping. Vite strips inline <script> tags from index.html that aren't module scripts, unless they're IIFE-wrapped (immediately-invoked function expression). The pattern above wraps in (function(){ ... })() so Vite preserves it.

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.

onMounted vs onActivated. If you use <KeepAlive> to cache the success page, onMounted fires once but onActivated fires on every cache hit. For a convert event you want onMounted (fires once).

SSR hydration. Nuxt 3 server-renders the success page, then hydrates client-side. onMounted only runs client-side, so the convert event always fires from the browser. No risk of double-firing from SSR.

Composition API only. The examples above use Vue 3's Composition API. If you're on Vue 2's Options API, use mounted() lifecycle hook with this.$route.query instead of useRoute().

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 calling Nuxt's build (not the dev server's HMR cache).

Vite preserves the script tag in dev but not in build. The IIFE wrapper is missing or malformed. Use the exact pattern above (no <script type="module">, no async/defer attributes on the script tag itself — those are inside the loader).

useRoute() returns query as undefined. Vue Router needs to be installed and configured. Confirm vue-router is in package.json and main.ts registers the router.

Convert fires before window.Rekomi is loaded. Slow network. Add a polling guard:

onMounted(async () => {
  let attempts = 0;
  while (!window.Rekomi && attempts++ < 50) {
    await new Promise(r => setTimeout(r, 100));
  }
  if (window.Rekomi) { /* fire convert */ }
});

Related

  • JavaScript pixel reference
  • Install on Stripe — for client_reference_id patterns
  • Install on React — for the React equivalent

Install on React (Vite, CRA, Remix)

Vite and CRA install in index.html; Remix and React Router 7 install in app/root.tsx. SPA navigation doesn't reload pages, so convert fires from a useEffect in your success route.

Install on Rails

Server-side S2S is the primary path for Rails apps. application.html.erb head install for click capture, RekomiClient service class for HMAC-signed conversion fires.

On this page

Vite Vue 3: install in index.htmlNuxt 3: install via nuxt.config.tsFire convert from onMountedNuxt server-side checkout via Server RoutesQuirks worth knowingTroubleshootingRelated