Skip to main content
These examples show the Tappd Web SDK applied to real-world scenarios. Each section is self-contained — copy the code, swap in your own values, and drop it into your project. All examples assume you have already initialized the SDK with your appId.
Track the full purchase funnel from product discovery through checkout, and capture cart abandonment for retargeting.Product purchase flow:
import { TappdSDK } from '@tappd/web-sdk';

const tappd = new TappdSDK({ appId: 'YOUR_APP_ID' });

// 1. User views a product
await tappd.track('ecommerce.product_viewed', {
  productId: 'prod_123',
  productName: 'iPhone 15',
  category: 'Electronics',
  price: 999.00,
  currency: 'USD'
});

// 2. User adds the product to their cart
await tappd.track('ecommerce.add_to_cart', {
  productId: 'prod_123',
  productName: 'iPhone 15',
  quantity: 1,
  price: 999.00,
  currency: 'USD'
});

// 3. User begins checkout
await tappd.track('ecommerce.checkout_started', {
  cartValue: 999.00,
  currency: 'USD'
});

// 4. User completes the purchase
await tappd.track('ecommerce.purchase', {
  orderId: 'ord_456',
  total: 999.00,
  currency: 'USD',
  items: [
    {
      productId: 'prod_123',
      name: 'iPhone 15',
      price: 999.00,
      quantity: 1
    }
  ]
});
Cart abandonment tracking:
async function trackCartAbandonment(cartItems) {
  await tappd.track('ecommerce.cart_abandoned', {
    cartValue: calculateTotal(cartItems),
    itemCount: cartItems.length,
    items: cartItems,
    currency: 'USD'
  });
}
Capture the complete sign-up and login lifecycle — including failures — so you can analyze drop-off and optimize your onboarding funnel.Sign-up flow:
async function handleSignUp(email, name) {
  // Track the attempt before any async work
  await tappd.track('user.signup.started', { method: 'email' });

  try {
    const user = await createAccount(email, name);

    // Identify the user — this merges all pre-signup anonymous events
    await tappd.identify({
      external_id: user.id,
      email: email,
      name: name,
      signupDate: new Date().toISOString()
    });

    // No userId needed — SDK auto-links to the identified user
    await tappd.track('user.signup.completed', { method: 'email' });
  } catch (error) {
    await tappd.track('user.signup.failed', {
      method: 'email',
      error: error.message
    });
  }
}
Login flow:
async function handleLogin(email) {
  await tappd.track('user.login.started', { method: 'email' });

  try {
    const user = await authenticate(email);

    await tappd.identify({
      external_id: user.id,
      email: user.email,
      name: user.name,
      lastLogin: new Date().toISOString()
    });

    await tappd.track('user.login.completed', { method: 'email' });
  } catch (error) {
    await tappd.track('user.login.failed', {
      method: 'email',
      error: error.message
    });
  }
}
Logout:
function handleLogout() {
  // Track the logout event before resetting
  tappd.track('user.logout');

  // Clear user identity — next events will be anonymous
  tappd.reset();
}
Instrument multi-step forms to understand where users engage, hesitate, or drop off.
// Step 1 — User lands on the form
await tappd.track('form.viewed', {
  formId: 'checkout-form',
  formName: 'Checkout'
});

// Step 2 — User starts filling in fields
await tappd.track('form.started', {
  formId: 'checkout-form',
  formName: 'Checkout'
});

// Step 3 — User completes an individual step
await tappd.track('form.step_completed', {
  formId: 'checkout-form',
  formName: 'Checkout',
  step: 'shipping',
  stepNumber: 1
});

// Step 4 — User submits the form
await tappd.track('form.submitted', {
  formId: 'checkout-form',
  formName: 'Checkout',
  success: true,
  fieldsCompleted: 5
});
Track plan changes and update user attributes in a single flow so your customer profiles stay current.Plan upgrade:
async function handleUpgrade(newPlan, userId) {
  const currentPlan = getUserPlan();

  await tappd.track('subscription.upgrade', {
    fromPlan: currentPlan,
    toPlan: newPlan,
    upgradeDate: new Date().toISOString()
  });

  // Sync the new plan to the user's Tappd profile
  // external_id must match the value used in identify()
  await tappd.setUserAttributes({
    external_id: userId,
    plan: newPlan,
    upgradedAt: new Date().toISOString()
  });
}
Subscription cancellation:
async function handleCancellation(reason, userId) {
  await tappd.track('subscription.cancelled', {
    reason: reason,
    cancelledAt: new Date().toISOString()
  });

  await tappd.setUserAttributes({
    external_id: userId,
    planStatus: 'cancelled',
    cancellationDate: new Date().toISOString()
  });
}
Use a lightweight hook for simple components, or set up a Context provider to share the SDK instance across your entire app.useTappd hook:
import { useEffect, useState } from 'react';
import { TappdSDK } from '@tappd/web-sdk';

function useTappd() {
  const [tappd] = useState(() => {
    return new TappdSDK({
      appId: process.env.REACT_APP_TAPPD_APP_ID
    });
  });

  return tappd;
}

// Usage inside a component
function ProductPage({ productId }) {
  const tappd = useTappd();

  useEffect(() => {
    tappd.track('product_viewed', { productId });
  }, [productId]);

  const handleAddToCart = async () => {
    await tappd.track('add_to_cart', { productId });
  };

  return <button onClick={handleAddToCart}>Add to Cart</button>;
}
React Context provider/consumer:
import React, { createContext, useContext } from 'react';
import { TappdSDK } from '@tappd/web-sdk';

const TappdContext = createContext(null);

// Wrap your app root with this provider
export function TappdProvider({ children }) {
  const tappd = new TappdSDK({
    appId: process.env.REACT_APP_TAPPD_APP_ID
  });

  return (
    <TappdContext.Provider value={tappd}>
      {children}
    </TappdContext.Provider>
  );
}

// Consume the shared instance anywhere in the tree
export function useTappd() {
  return useContext(TappdContext);
}
Create a composable that initializes the SDK on mount and exposes it reactively throughout your Vue app.
// composables/useTappd.js
import { ref, onMounted } from 'vue';
import { TappdSDK } from '@tappd/web-sdk';

export function useTappd() {
  const tappd = ref(null);

  onMounted(() => {
    tappd.value = new TappdSDK({
      appId: import.meta.env.VITE_TAPPD_APP_ID
    });
  });

  return tappd;
}
<!-- Usage inside a component -->
<script setup>
import { useTappd } from '@/composables/useTappd';

const tappd = useTappd();

const handleClick = async () => {
  await tappd.value?.track('button_click', { buttonId: 'cta' });
};
</script>

<template>
  <button @click="handleClick">Get Started</button>
</template>
Initialize the SDK in your root layout using useEffect and clean up with reset() when the component unmounts.
// app/layout.tsx
'use client';

import { useEffect } from 'react';
import { TappdSDK } from '@tappd/web-sdk';

export default function RootLayout({
  children
}: {
  children: React.ReactNode
}) {
  useEffect(() => {
    const tappd = new TappdSDK({
      appId: process.env.NEXT_PUBLIC_TAPPD_APP_ID as string
    });

    // Make the instance globally available if needed
    (window as any).tappd = tappd;

    // Clean up on unmount
    return () => {
      tappd.reset();
    };
  }, []);

  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  );
}
For the Pages Router, initialize the SDK inside _app.js so the instance is created once and shared across all pages.
// pages/_app.js
import { useEffect } from 'react';
import { TappdSDK } from '@tappd/web-sdk';

function MyApp({ Component, pageProps }) {
  useEffect(() => {
    const tappd = new TappdSDK({
      appId: process.env.NEXT_PUBLIC_TAPPD_APP_ID
    });

    // Make the instance globally available if needed
    window.tappd = tappd;
  }, []);

  return <Component {...pageProps} />;
}

export default MyApp;
Capture unhandled JavaScript errors and promise rejections to monitor application health alongside your user behavior data.
// Capture synchronous runtime errors
window.addEventListener('error', (event) => {
  tappd.track('error', {
    message: event.message,
    filename: event.filename,
    lineno: event.lineno,
    colno: event.colno,
    stack: event.error?.stack
  });
});

// Capture unhandled promise rejections
window.addEventListener('unhandledrejection', (event) => {
  tappd.track('error.unhandled_promise_rejection', {
    reason: String(event.reason),
    stack: event.reason?.stack
  });
});
Track experiment exposure and conversions to measure the impact of each variant directly within Tappd.
// Assign and record the variant at experiment entry
const variant = getExperimentVariant('homepage-cta');

await tappd.track('experiment.viewed', {
  experimentId: 'homepage-cta',
  variant: variant
  // No userId needed — SDK auto-associates with the identified user
});

// Record a conversion later in the user flow
await tappd.track('experiment.converted', {
  experimentId: 'homepage-cta',
  variant: variant,
  conversionType: 'signup'
});
Instrument native HTML5 <video> elements to measure play rate, pause behavior, and completion rate.
const video = document.querySelector('video');

video.addEventListener('play', () => {
  tappd.track('video.played', {
    videoId: 'intro-video',
    videoTitle: 'Product Introduction'
  });
});

video.addEventListener('pause', () => {
  tappd.track('video.paused', {
    videoId: 'intro-video',
    currentTime: video.currentTime,
    duration: video.duration,
    percentWatched: Math.round((video.currentTime / video.duration) * 100)
  });
});

video.addEventListener('ended', () => {
  tappd.track('video.completed', {
    videoId: 'intro-video',
    duration: video.duration
  });
});