Thisbefine
Guides

Analytics

Track what users actually do. Not just what you hope they do.

Analytics

Track what users do. Which features get used, where people drop off, what leads to conversions.

Tracking Events

The track() Method

import { useTrack } from '@thisbefine/analytics/next';

const MyComponent = () => {
  const trackUpgrade = useTrack('plan_upgraded');

  const handleUpgrade = (plan: string) => {
    trackUpgrade({
      previousPlan: 'free',
      newPlan: plan,
      source: 'pricing_page',
    });
  };

  return <button onClick={() => handleUpgrade('pro')}>Upgrade</button>;
};
import { getAnalytics } from '@thisbefine/analytics';

const analytics = getAnalytics();

analytics?.track('plan_upgraded', {
  previousPlan: 'free',
  newPlan: 'pro',
  source: 'pricing_page',
});

Event Properties

Properties give context. Numbers, strings, booleans, arrays, objects all work:

track('purchase_completed', {
  amount: 99.99,
  quantity: 1,
  currency: 'USD',
  productId: 'prod_123',
  isFirstPurchase: true,
  items: ['item_1', 'item_2'],
  metadata: { campaign: 'summer_sale' },
});

Keep property values under 1KB when serialized.

Event Naming

Use object_action format with snake_case

// Good
track('button_clicked');
track('form_submitted');
track('signup_completed', { plan: 'pro' });

// Bad
track('click');
track('ButtonClickedEvent');
track('button_clicked', { button: 'signup' });

Tracking Pageviews

Automatic Tracking

The <Analytics /> component tracks pageviews automatically by default:

<Analytics apiKey="tif_xxx" trackPageviews={true} />

For Next.js, the /next export uses usePathname to track all client-side navigation. For plain React, it catches popstate events (browser back/forward).

Manual Pageview Tracking

For single-page apps with fancy routing:

import { usePage } from '@thisbefine/analytics/next';
import { usePathname } from 'next/navigation';
import { useEffect } from 'react';

const PageTracker = () => {
  const page = usePage();
  const pathname = usePathname();

  useEffect(() => {
    page(pathname, {
      section: pathname.split('/')[1],
    });
  }, [pathname, page]);

  return null;
};
import { getAnalytics } from '@thisbefine/analytics';

getAnalytics()?.page('Dashboard', {
  section: 'analytics',
  tab: 'overview',
});

Automatic Pageview Data

PropertyValue
urlFull URL
pathPath without query params
referrerPrevious page
titleDocument title

Add custom properties:

page('Product Page', { productId: 'prod_123', category: 'electronics' });

Common Patterns

E-commerce

track('product_viewed', { productId: 'prod_123', name: 'Widget Pro', price: 49.99 });
track('cart_item_added', { productId: 'prod_123', quantity: 1, cartTotal: 49.99 });
track('checkout_started', { cartItems: 3, cartTotal: 149.97 });
track('purchase_completed', { orderId: 'ord_456', total: 149.97, items: 3 });

SaaS

track('feature_used', { feature: 'export', format: 'csv', rowCount: 1000 });
track('limit_reached', { limit: 'api_calls', current: 10000, max: 10000 });
track('settings_updated', { setting: 'notifications', oldValue: true, newValue: false });

User Journey

track('onboarding_step_completed', { step: 1, stepName: 'profile_setup', totalSteps: 5 });
track('onboarding_completed', { timeToComplete: 180, skippedSteps: [] });
track('activation_milestone', { milestone: 'first_project_created', daysFromSignup: 2 });

Event Batching

Events are batched for efficiency:

  • Send when 20 are queued (configurable via flushAt)
  • Send every 10 seconds (configurable via flushInterval)
  • Flush on page unload

Force Immediate Send

await getAnalytics()?.flush();
window.location.href = '/external-page';

Privacy

Do Not Track

Respects browser DNT setting by default. When DNT is on, nothing is tracked.

<Analytics apiKey="tif_xxx" config={{ respectDNT: true }} />

Opt-Out

import { useAnalytics } from '@thisbefine/analytics/next';

const PrivacySettings = () => {
  const analytics = useAnalytics();

  return (
    <div>
      <button onClick={() => analytics.optOut()}>Disable</button>
      <button onClick={() => analytics.optIn()}>Enable</button>
      <p>Status: {analytics.isOptedOut() ? 'Opted Out' : 'Active'}</p>
    </div>
  );
};

Opt-out preference persists in localStorage.

On this page