twelvebitDocsllms.txtSign in
View as Markdown

Install the script

The script is one tag. It is about 5 KB gzipped, loads with defer, and does not block the page. Put it in the <head> so it loads on every page.

html
<script defer data-website-id="tb_YOUR_SITE_ID" data-domain="example.com" src="https://twelvebit.app/js/script.js"></script>

Find your site id on the site's setup page in the dashboard. It starts with tb_.

Plain HTML#

Paste the tag into the <head> of every page, or of your shared template.

Next.js (App Router)#

Use next/script in the root layout. afterInteractive loads the script after hydration.

tsx
// app/layout.tsx
import Script from "next/script";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        {children}
        <Script
          src="https://twelvebit.app/js/script.js"
          data-website-id="tb_YOUR_SITE_ID"
          data-domain="example.com"
          strategy="afterInteractive"
        />
      </body>
    </html>
  );
}

Next.js (Pages Router)#

tsx
// pages/_app.tsx
import type { AppProps } from "next/app";
import Script from "next/script";

export default function App({ Component, pageProps }: AppProps) {
  return (
    <>
      <Script
        src="https://twelvebit.app/js/script.js"
        data-website-id="tb_YOUR_SITE_ID"
        data-domain="example.com"
        strategy="afterInteractive"
      />
      <Component {...pageProps} />
    </>
  );
}

You can also put a plain <script defer …> tag inside <Head> in pages/_document.tsx.

React with Vite (or Create React App)#

Add the tag to index.html in the project root (public/index.html for Create React App):

html
<!-- index.html -->
<head>
  <script defer data-website-id="tb_YOUR_SITE_ID" data-domain="example.com" src="https://twelvebit.app/js/script.js"></script>
</head>

The script follows client-side navigation by itself (pushState, replaceState and the back button).

Vue and Nuxt#

Vue with Vite: add the tag to index.html, as for React.

Nuxt 3 and 4:

ts
// nuxt.config.ts
export default defineNuxtConfig({
  app: {
    head: {
      script: [
        {
          src: "https://twelvebit.app/js/script.js",
          defer: true,
          "data-website-id": "tb_YOUR_SITE_ID",
          "data-domain": "example.com",
        },
      ],
    },
  },
});

Svelte and SvelteKit#

SvelteKit: add the tag to src/app.html, inside <head>, before %sveltekit.head%. Svelte with Vite: add it to index.html.

html
<!-- src/app.html -->
<head>
  <script defer data-website-id="tb_YOUR_SITE_ID" data-domain="example.com" src="https://twelvebit.app/js/script.js"></script>
  %sveltekit.head%
</head>

Astro#

Add the tag to the <head> of your base layout. is:inline tells Astro to leave the tag as it is.

astro
---
// src/layouts/Layout.astro
---
<html lang="en">
  <head>
    <script is:inline defer data-website-id="tb_YOUR_SITE_ID" data-domain="example.com" src="https://twelvebit.app/js/script.js"></script>
  </head>
  <body><slot /></body>
</html>

With view transitions (<ClientRouter />), the script still counts each page, because Astro changes the URL with pushState.

Remix and React Router 7#

tsx
// app/root.tsx
import { Links, Meta, Outlet, Scripts, ScrollRestoration } from "react-router"; // "@remix-run/react" in Remix 2

export function Layout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <head>
        <Meta />
        <Links />
        <script defer data-website-id="tb_YOUR_SITE_ID" data-domain="example.com" src="https://twelvebit.app/js/script.js" />
      </head>
      <body>
        {children}
        <ScrollRestoration />
        <Scripts />
      </body>
    </html>
  );
}

export default function App() {
  return <Outlet />;
}

Gatsby#

jsx
// gatsby-ssr.jsx
import * as React from "react";

export const onRenderBody = ({ setHeadComponents }) => {
  setHeadComponents([
    <script
      key="twelvebit"
      defer
      data-website-id="tb_YOUR_SITE_ID"
      data-domain="example.com"
      src="https://twelvebit.app/js/script.js"
    />,
  ]);
};

WordPress#

Use a header code plugin (for example WPCode: Code Snippets → Header & Footer → Header) and paste the tag. Or add it from your theme's functions.php (use a child theme, so theme updates keep it):

php
// functions.php
add_action('wp_head', function () {
  echo '<script defer data-website-id="tb_YOUR_SITE_ID" data-domain="example.com" src="https://twelvebit.app/js/script.js"></script>';
});

You can also paste the tag into header.php, before </head>.

For a WooCommerce store, use the twelvebit plugin instead: it adds the tag, the cart and checkout goals, and the orders.

Shopify#

Online Store → Themes → … → Edit code → layout/theme.liquid. Paste the tag before </head> and save. Checkout pages on Shopify do not load theme code, so use Revenue to count orders.

Or skip the theme edit: connect the store with a custom app. twelvebit then installs the script itself, sends add_to_cart and initiate_checkout goals, and records each paid order.

Webflow#

Site settings → Custom code → Head code. Paste the tag, save, and publish the site. Custom code needs a paid site plan.

Framer#

Site Settings → General → Custom Code → "End of <head> tag". Paste the tag and publish.

Google Tag Manager#

GTM can drop data-* attributes from a plain script tag. Use a Custom HTML tag that creates the script element, with the trigger All Pages:

html
<script>
  (function () {
    var s = document.createElement("script");
    s.defer = true;
    s.src = "https://twelvebit.app/js/script.js";
    s.setAttribute("data-website-id", "tb_YOUR_SITE_ID");
    s.setAttribute("data-domain", "example.com");
    document.head.appendChild(s);
  })();
</script>

After you install#

  • Open the site and check the dashboard, or run the install check in Quickstart.
  • To send events before the script has loaded, add the queue stub from the Script reference.
  • If ad blockers are common with your visitors, serve the script from your own domain. See Proxy.