Stickybeak Docs Dashboard Get early access

Frameworks

Expo and React Native Web

Expo analytics with Stickybeak works on Expo web builds, not native iOS or Android apps; this guide shows where to add the script and how to track page views, goals and purchase revenue. It is a lighter alternative to Google Analytics for Expo web, with live visitors, goals and revenue attribution in one script.

Add the script

Expo Router with static or server output

For web.output: "static" or "server", create src/app/+html.tsx (or app/+html.tsx for root routes):

import { ScrollViewStyleReset } from "expo-router/html";
import type { PropsWithChildren } from "react";

export default function Root({ children }: PropsWithChildren) {
  const siteId = process.env.EXPO_PUBLIC_STICKYBEAK_SITE_ID;

  return (
    <html lang="en">
      <head>
        <meta charSet="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <ScrollViewStyleReset />
        {siteId && (
          <script
            defer
            data-site={siteId}
            data-domain="example.com"
            src="https://app.usestickybeak.com/_cfw/s.js"
          />
        )}
      </head>
      <body>{children}</body>
    </html>
  );
}

Set EXPO_PUBLIC_STICKYBEAK_SITE_ID when you run npx expo export --platform web for production, and leave it unset in development.

Single-page output

With web.output: "single", all routes share one HTML template. Create it with:

npx expo customize public/index.html

Then paste the standard snippet before </head> in public/index.html:

Paste the same script tag before </head> in public/index.html.

Check it's working

Test the deployed site, then verify your install.

Track goals

React Native components cannot use data-goal; call the web-only typed helper below. See Custom goals.

import { Platform } from "react-native";

type GoalProps = Record<string, string | number | boolean>;

declare global {
  interface Window {
    stickybeak?: (goal: string, props?: GoalProps) => void;
  }
}

export function track(goal: string, props?: GoalProps): void {
  if (Platform.OS !== "web" || typeof window === "undefined") return;
  window.stickybeak?.(goal, props);
}
import { Pressable, Text } from "react-native";
import { track } from "@/lib/stickybeak";

export function UpgradeButton() {
  return (
    <Pressable onPress={() => track("upgrade_click", { plan: "pro" })}>
      <Text>Upgrade to Pro</Text>
    </Pressable>
  );
}

Track purchases

After confirming an order in the browser, send:

window.stickybeak?.("purchase", { revenue: 49, currency: "USD", order_id: "ORDER_ID" });

Use real values; see Revenue.

Page changes

Expo web counts new paths, including back and forward; router.replace() does not. See Single-page apps.

If it isn't working

Check the production site ID and domain, then see Troubleshooting.