supapulsedocs
React packageHooks reference

Hooks reference

useSupapulse, useEvent, useIdentify and usePageview from @supapulse/react, with their exact behavior and the edge cases.

All hooks must be called inside a <SupapulseProvider>. Outside one, useSupapulse (and the hooks built on it) throw [Supapulse React] useSupapulse must be used within a SupapulseProvider.

useSupapulse

function useSupapulse(): SupapulseCore;

Returns the provider's tracker, with every core method: event, pageview, getSessionId and so on.

The tracker exists from the first render, but the provider calls init() in an effect. A call made before that is dropped, with a [Supapulse] Not initialized warning in the console. Event handlers run after init and are safe. Code that runs in a mount effect should wait for isReady from the context:

import { SupapulseContext, useSupapulse } from '@supapulse/react';
import { useContext, useEffect } from 'react';

function TrackOnMount({ name }: { name: string }) {
  const supapulse = useSupapulse();
  const isReady = useContext(SupapulseContext)?.isReady;

  useEffect(() => {
    if (!isReady) return;
    supapulse.event(name);
  }, [supapulse, isReady, name]);

  return null;
}

A child's mount effect runs before the provider's, so without the isReady check the first call would be dropped.

Reading the visitor fingerprint and session ID for checkout:

function BuyButton({ priceId }: { priceId: string }) {
  const supapulse = useSupapulse();

  async function buy() {
    const response = await fetch('/api/checkout', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        priceId,
        fingerprint: await supapulse.getVisitorFingerprint(),
        sessionId: supapulse.getSessionId(),
      }),
    });
    location.href = (await response.json()).url;
  }

  return <button onClick={buy}>Buy</button>;
}

useEvent

function useEvent(): (eventName: string, eventData?: EventData) => Promise<void>;

Returns a memoized function that sends a custom event.

function PlanPicker() {
  const trackEvent = useEvent();
  return (
    <button onClick={() => trackEvent('plan_selected', { plan: 'growth' })}>
      Choose Growth
    </button>
  );
}

Called before the tracker is ready, the function sends nothing and the tracker logs a warning. Use it from event handlers, or wait for isReady in effects.

useIdentify

function useIdentify(): (customerId: string) => Promise<void>;

Returns a memoized function that links the current visitor to your customer ID. Call it once the user is known, so revenue sent for that ID is credited to the visit that brought them. See Revenue attribution.

import { SupapulseContext, useIdentify } from '@supapulse/react';
import { useContext, useEffect } from 'react';

function IdentifyUser({ userId }: { userId: string | null }) {
  const identify = useIdentify();
  const isReady = useContext(SupapulseContext)?.isReady;

  useEffect(() => {
    if (isReady && userId) identify(userId);
  }, [identify, isReady, userId]);

  return null;
}

Identify events never count toward your usage. Like useEvent, calling it before the tracker is ready sends nothing.

usePageview

function usePageview(options?: { trigger?: unknown; disabled?: boolean }): void;

Sends pageviews from React instead of the core tracker's History API listener. It only works with autoPageviews: false. With the default autoPageviews: true the core tracker already records every pageview, so the hook does nothing.

OptionBehavior
noneOne pageview when the tracker becomes ready.
triggerA pageview when the tracker becomes ready and every time trigger changes. Pass the path. Safe under React StrictMode.
disabled: trueSends nothing.
import { useLocation } from 'react-router-dom';

const config = { apiToken: 'pub_your_token', autoPageviews: false };

function RouteTracker() {
  const location = useLocation();
  usePageview({ trigger: location.pathname });
  return null;
}

The pageview reads location.href and document.title at the time the effect runs.

Testing components that use the hooks

Wrap them in the context with a fake tracker instead of the real provider:

import { SupapulseContext } from '@supapulse/react';
import { render } from '@testing-library/react';
import { vi } from 'vitest';

const fake = {
  init: vi.fn(),
  pageview: vi.fn(),
  event: vi.fn(),
  identify: vi.fn(),
  getSessionId: () => 'test-session',
  getVisitorFingerprint: async () => null,
  use: vi.fn(),
};

render(
  <SupapulseContext.Provider value={{ supapulse: fake, isReady: true, autoPageviews: true, error: null }}>
    <PlanPicker />
  </SupapulseContext.Provider>,
);