Developer Docs / Web React SDK / Sponsorships

Web React Sponsorship Rendering

Render the current sponsor or public availability state with GrowthCat's built-in banner, a custom React design, or the headless TypeScript API.

React exports

GrowthCatSponsorBanner and useSponsor.

Headless

Load stable render data and track qualified impressions and clicks.

Disclosure

Every live sponsor design must clearly say Sponsored.

1. Overview

Configure a sponsor placement in the GrowthCat dashboard, then use its stable slotKey on the site. The SDK returns only SDK-safe public rendering data—not private sponsor email, receipt, or publisher information.

Qualified sponsor impressions

A sponsor impression requires at least 50% continuous visibility for one second. The built-in component measures this automatically; custom renderers must measure it before tracking.

2. Install And Initialize

bash
npm install @growthcat/web react react-dom
# or
yarn add @growthcat/web react react-dom
ts
import { GrowthCat } from "@growthcat/web";

GrowthCat.initialize({
  apiKey: process.env.NEXT_PUBLIC_GROWTHCAT_KEY!,
  workspace: "live",
  logsEnabled: process.env.NODE_ENV !== "production",
  measurementMode: "essential",
});

Install the GrowthCat integration skill

The WebSDK repository includes an agent skill that inspects your app, checks the installed SDK types, and adds referrals, attribution, ads, sponsorships, or feedback using your existing architecture.

bash
npx skills add . --skill growthcat-web-integration

Run this from a local GrowthCatSDK-Web checkout. For a remote install, replace the dot with the repository Git URL. Then tell your coding agent which capability and placement you want.

Use $growthcat-web-integration to add a feedback board to my settings page.

3. Built-in Sponsor Banner

The component renders a live sponsor or an optional public booking prompt, opens the correct destination, and tracks qualified events automatically.

tsx
import { GrowthCat } from "@growthcat/web";
import { GrowthCatSponsorBanner } from "@growthcat/web/react";

export function HomePage() {
  return (
    <GrowthCatSponsorBanner
      slotKey="home_sponsor"
      sessionId={GrowthCat.makeSessionId()}
      showAvailability
      onTap={() => console.log("Sponsor opened")}
    />
  );
}

Set showAvailability to false when available slots should render nothing instead of a booking prompt.

4. Custom Sponsor UI With useSponsor

useSponsor() keeps one stable creative instance ID for the fetched sponsor. Reuse it for the impression and click; call reload only when you intentionally want a new presentation.

tsx
import { useEffect, useRef } from "react";
import { useSponsor } from "@growthcat/web/react";

export function CustomSponsor({ slotKey }: { slotKey: string }) {
  const ref = useRef<HTMLDivElement>(null);
  const { sponsor, trackImpression, trackClick } = useSponsor({ slotKey });

  useEffect(() => {
    if (sponsor?.status !== "live" || !ref.current) return;
    let timer: ReturnType<typeof setTimeout> | undefined;
    const observer = new IntersectionObserver(([entry]) => {
      clearTimeout(timer);
      if (entry.intersectionRatio >= 0.5 && document.visibilityState === "visible") {
        timer = setTimeout(() => {
          void trackImpression(entry.intersectionRatio, 1000);
        }, 1000);
      }
    }, { threshold: 0.5 });
    observer.observe(ref.current);
    return () => { clearTimeout(timer); observer.disconnect(); };
  }, [sponsor, trackImpression]);

  if (sponsor?.status !== "live" || !sponsor.creative) return null;
  const creative = sponsor.creative;

  return (
    <div ref={ref}>
      <small>Sponsored</small>
      {creative.logoUrl && <img src={creative.logoUrl} alt="" />}
      <strong>{creative.headline ?? creative.sponsorName}</strong>
      {creative.body && <p>{creative.body}</p>}
      <button onClick={async () => {
        await trackClick();
        if (creative.clickUrl) {
          window.open(creative.clickUrl, "_blank", "noopener,noreferrer");
        }
      }}>
        {creative.ctaText ?? "Learn more"}
      </button>
    </div>
  );
}

5. Headless Sponsor API

Use the headless API outside React or when another framework owns rendering. Keep the returned object intact so its creative instance ID remains stable.

ts
const sponsor = await GrowthCat.shared.loadSponsorData("home_sponsor");

if (sponsor.status === "live" && sponsor.creative) {
  renderSponsor(sponsor.creative);

  await GrowthCat.shared.trackSponsorImpression(sponsor, {
    visibleFraction: 0.8,
    visibleDurationMs: 1100,
  });

  await GrowthCat.shared.trackSponsorClick(sponsor);
}

Use sponsor(slotKey) only when you need the raw slot response and do not need a stable render identity.

6. Sponsor Response States

StatusRecommended UI
"live"Render creative and track qualified events.
"available"Optionally show priceUsd, bookingUrl, and nextAvailablePeriods.
"empty"Render nothing because the availability placeholder is disabled.

7. API Reference

ts
// Headless client
client.sponsor(slotKey): Promise<SponsorSlotContent>
client.loadSponsorData(slotKey): Promise<GrowthCatSponsorData>
client.trackSponsorImpression(data, options): Promise<boolean>
client.trackSponsorClick(data, options?): Promise<boolean>
client.trackSponsorEvent(slotKey, "impression" | "click", options?): Promise<void>
client.flushSponsorEvents(): Promise<void>

// React
useSponsor(options): UseSponsorResult
<GrowthCatSponsorBanner
  slotKey="home_sponsor"
  sessionId={sessionId}
  showAvailability
  onTap={handleTap}
/>