# SDKs

Use an SDK when a script tag is not a good fit: a web app that you build with npm, or a React Native or Expo app. The SDKs send the same events to the same endpoint as the [script](/docs/script), so the dashboard, goals, identify and revenue work the same way.

| Package | For | Install |
|---|---|---|
| `@twelvebit/web` | React, Next.js, Vue, Svelte and other apps built with npm | `npm i https://twelvebit.app/sdk/twelvebit-web.tgz` |
| `@twelvebit/react-native` | React Native and Expo apps | `npm i https://twelvebit.app/sdk/twelvebit-react-native.tgz` |

Both packages have no dependencies and include TypeScript types. Use the SDK or the script on a page, not both, or each page view counts two times.

## Web: @twelvebit/web

~~~sh
npm i https://twelvebit.app/sdk/twelvebit-web.tgz
~~~

Call `initTwelvebit` one time in the browser. It sends a page view at once and on each client-side navigation (`pushState`, `replaceState`, back and forward).

~~~ts
import { initTwelvebit, track, identify } from "@twelvebit/web";

initTwelvebit({ siteId: "tb_YOUR_SITE_ID", domain: "example.com" });

track("signup", { plan: "pro" });
identify("user_42", { email: "ann@example.com", name: "Ann" });
~~~

The package is safe to import on the server. On the server, every call does nothing. Calls before `initTwelvebit` wait (at most 50) and run after it.

### React and Next.js

Put the provider in the root layout. It starts the client in the browser after the first render.

~~~tsx
// app/layout.tsx
import { TwelvebitProvider } from "@twelvebit/web/react";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <TwelvebitProvider config={{ siteId: "tb_YOUR_SITE_ID", domain: "example.com" }}>{children}</TwelvebitProvider>
      </body>
    </html>
  );
}
~~~

~~~tsx
"use client";
import { useTwelvebit } from "@twelvebit/web/react";

export function UpgradeButton() {
  const tb = useTwelvebit();
  return <button onClick={() => tb.track("upgrade_click", { plan: "pro" })}>Upgrade</button>;
}
~~~

### Web options

| Option | Default | What it does |
|---|---|---|
| `siteId` | (required) | The site id from the dashboard (`tb_…`). |
| `domain` | none | Root domain for the cookies, so that subdomains share one visitor. |
| `apiUrl` | `https://twelvebit.app` | Where events go (`{apiUrl}/api/e`). Set it to your [proxy](/docs/proxy) to send events through your own domain. |
| `autoPageviews` | `true` | Page views on load and on SPA navigation. `{ hash: true }` also counts hash changes. `false`: call `pageview()` yourself. |
| `allowLocalhost` | `false` | Track on `localhost`, `*.local` and `*.test`. |
| `allowIframe` | `false` | Track inside an iframe. |
| `cookieless` | `false` | Set no cookies. See [cookieless mode](/docs/privacy). |
| `allowedHostnames` | `[]` | Your other domains. Ids from links built with `buildCrossDomainUrl` on these domains are adopted. |
| `disablePayments` | `false` | Do not send payment hints from checkout success URLs (Stripe `session_id`, Polar `checkout_id`, Lemon Squeezy `order_id`). |
| `debug` | `false` | Log each event to the console. |

### Web API

| Function | What it does |
|---|---|
| `initTwelvebit(config)` | Starts the shared client and returns it. Later calls return the same client. |
| `track(name, props?)` | Sends a goal. Names are stored in lower case, at most 64 characters. At most 30 props; strings are cut at 255 characters. |
| `pageview(url?)` | Sends a page view for the current URL, or for a path or URL that you give. |
| `identify(userId, traits?)` | Links the visitor to your user. Traits can include `email`, `name` and `image`. See [Identify](/docs/identify). |
| `payment({ email })` | A payment hint from your success page, so that the payment finds this visitor. See [Revenue](/docs/revenue). |
| `reset()` | Starts a new visitor and session. Call it at logout. |
| `getVisitorId()` | The visitor id, or `null` (server, cookieless, not started). |
| `getTrackingParams()` | `{ _tb_vid, _tb_sid }`, for example to add to a checkout's metadata. |
| `buildCrossDomainUrl(url)` | The URL with `_tb_vid` and `_tb_sid` added, for a link to one of your `allowedHostnames`. |
| `flush()` | Sends queued events now. |
| `createTwelvebit(config)` | A separate client that is not shared. It has the same methods and `destroy()`. |

How it sends: events wait in a queue and go out every 5 seconds or at 10 events, one request per event. When the page is hidden or closed, the queue goes out with `sendBeacon`. The cookies (`_tb_vid` 1 year, `_tb_sid` 30 minutes) and the tracking gates (localhost, automated browsers, `localStorage.twelvebit_ignore`) are the same as the script.

## React Native and Expo: @twelvebit/react-native

~~~sh
npm i https://twelvebit.app/sdk/twelvebit-react-native.tgz
# Recommended: keeps the visitor id and the offline queue when the app restarts, and sends when the network comes back.
npx expo install @react-native-async-storage/async-storage @react-native-community/netinfo
~~~

Without AsyncStorage, the SDK keeps the visitor id and the queue in memory only, so each app start is a new visitor. NetInfo is optional: without it, the SDK retries with backoff.

~~~tsx
import { TwelvebitProvider, useTwelvebit, useTwelvebitScreen } from "@twelvebit/react-native";

export default function App() {
  return (
    <TwelvebitProvider config={{ siteId: "tb_YOUR_SITE_ID", appName: "myapp" }}>
      <Home />
    </TwelvebitProvider>
  );
}

function Home() {
  useTwelvebitScreen("Home");
  const tb = useTwelvebit();
  return <Button title="Buy" onPress={() => tb.track("purchase_click", { sku: "pro" })} />;
}
~~~

With React Navigation, send each screen from the navigation container:

~~~tsx
const ref = useNavigationContainerRef();
const tb = useTwelvebit();

<NavigationContainer ref={ref} onStateChange={() => tb.trackScreen(ref.getCurrentRoute()?.name ?? "unknown")}>
~~~

Without React, use the client directly:

~~~ts
import { createTwelvebit } from "@twelvebit/react-native";

const tb = createTwelvebit({ siteId: "tb_YOUR_SITE_ID", appName: "myapp" });
tb.trackScreen("Settings");
~~~

### How app events look in the dashboard

- A screen is a page view with the URL `app://<appName>/<screen>`. The dashboard shows the hostname `app` and the path `/<screen>`. Filter on the hostname `app` to see only app traffic.
- The device is Mobile or Tablet (iPad, and Android with a short side of 600 dp or more). The operating system comes from the User-Agent that the SDK sends: `Mozilla/5.0 (…) twelvebit-react-native/0.1 (ios)`.
- A screen view has no referrer. App traffic shows as Direct.
- The server accepts `app://` URLs on every site, so you can use the same site id for your website and your app. To link one person across the two, call `identify` with the same user id on the web and in the app.

### React Native options

| Option | Default | What it does |
|---|---|---|
| `siteId` | (required) | The site id from the dashboard (`tb_…`). |
| `apiUrl` | `https://twelvebit.app` | Where events go (`{apiUrl}/api/e`). |
| `appName` | `app` | Used in screen URLs: `app://<appName>/<screen>`. |
| `storage` | AsyncStorage, else memory | Any object with async `getItem` and `setItem`. Use it for MMKV or a custom store. |
| `flushInterval` | `5000` | Milliseconds before queued events go out. When the app goes to the background, and at 10 events, they go out at once. |
| `disabled` | `false` | Send nothing, for example until the user gives consent. |
| `device` | detected | `"Mobile"` or `"Tablet"`. |
| `debug` | `false` | Log each event to the console. |

### React Native API

| Function | What it does |
|---|---|
| `<TwelvebitProvider config client?>` | Creates one client per site (a re-mount or strict mode does not make a second one). |
| `useTwelvebit()` | The client from the provider. |
| `useTwelvebitScreen(name, props?)` | Sends a screen view on mount and when `name` changes. |
| `trackScreen(name, props?)` | Sends a page view for the screen. |
| `track(name, props?)` | Sends a goal (the same rules as on the web). |
| `identify(userId, traits?)` | Links this device's visitor to your user. |
| `payment({ email })` | A payment hint, for example after an in-app checkout. |
| `reset()` | A new visitor and session. Call it at logout. |
| `flush()` | Sends queued events now and resolves when done. |
| `getVisitorId()`, `getSessionId()`, `queueSize()` | The current ids, and the number of events that wait. |

Offline and sessions: events wait in a queue that is kept in storage (at most 200; the oldest go first). A failed request is tried again after 1, 2, 4 … seconds (at most 5 minutes). The server never accepts a `4xx` answer, so the SDK drops that event. A session ends after 30 minutes without events or time in the foreground. When the app goes to the background, the SDK sends a ping, so the visit duration includes the time in the app.

---

Source: https://twelvebit.app/docs/sdks · All docs: https://twelvebit.app/llms.txt
