Skip to main content
Puntego installs with a single script tag, but most apps want a tighter integration: a layout that mounts the widget once, declared targets that raise action success, and a no-code path for non-engineers. This page covers the common stacks. If you just want the raw script tag, see the install guide.
Availability. The script-tag and Google Tag Manager paths work today. The @puntego/react and @puntego/embed npm packages are rolling out — if npm install 404s, the packages have not published yet; use the script tag in the meantime. The GTM template is available from puntego.com/gtm/template.tpl and the GTM Community Template Gallery.

Next.js (App Router)

Install the React wrapper:
npm install @puntego/react @puntego/embed react
Mount the provider once in your root layout. Because <PuntegoProvider> only loads the widget in a client effect, it is safe to render on the server.
// app/layout.tsx
import { PuntegoProvider } from '@puntego/react';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <PuntegoProvider appId="gp_your_app_id" apiUrl="https://worker.puntego.com">
          {children}
        </PuntegoProvider>
      </body>
    </html>
  );
}

CSP nonce

If you serve a strict Content Security Policy with a per-request nonce, read it from next/headers and pass it to the provider. It is propagated to both nonce and data-nonce on the boot script.
import { headers } from 'next/headers';
import { PuntegoProvider } from '@puntego/react';

export default async function RootLayout({ children }: { children: React.ReactNode }) {
  const nonce = (await headers()).get('x-nonce') ?? undefined;
  return (
    <html lang="en">
      <body>
        <PuntegoProvider appId="gp_your_app_id" nonce={nonce}>
          {children}
        </PuntegoProvider>
      </body>
    </html>
  );
}
Allow the boot origin in script-src and the worker origin in connect-src. See CSP for the full policy.

Declare targets

Declared targets are the highest-priority grounding tier — the guide resolves them before falling back to DOM heuristics, which measurably raises action success. Declare the elements that matter on each page; the hook registers on mount and unregisters on unmount.
'use client';

import { useRef } from 'react';
import { usePuntegoTarget } from '@puntego/react';

export function CheckoutButton() {
  const ref = useRef<HTMLButtonElement>(null);
  usePuntegoTarget(ref, { id: 'checkout', description: 'Primary checkout CTA' });
  return <button ref={ref} type="button">Check out</button>;
}

React

The same @puntego/react package works in any React 18+ app (Vite, CRA, Remix). Wrap your app once in <PuntegoProvider>, then use usePuntegoTarget and usePuntegoAction to declare targets and actions per component. Use usePuntego() for imperative calls such as point or sendMessage.

Single-page apps and client-side routing

The runtime hooks history.pushState and popstate, so client-side route changes are detected automatically — there is nothing to wire up for React Router, Vue Router, or SvelteKit navigation. The guide re-reads the page on each route change, and an in-progress tour resumes across navigations. If you mount and unmount declared targets across routes, prefer the framework hooks (or call window.Puntego.registerTarget / unregisterTarget from your own lifecycle) so the registry always matches what is on screen.

Google Tag Manager (no code)

If you cannot edit your site’s HTML but you have a GTM container, use the community template.
  1. In GTM, open Templates → Tag Templates → New, then import the template. Download it from puntego.com/gtm/template.tpl, or find Puntego Embed in the GTM Community Template Gallery.
  2. Add a Puntego Embed tag and fill in your App ID and Worker API origin (copy both from Dashboard → Install).
  3. Trigger on All Pages (Initialization), then Preview and Publish.
The tag injects boot.js once and publishes a window.__PUNTEGO_CONFIG__ object that the boot script reads when no data-app-id attribute is present. The GTM and hand-pasted paths share the same runtime.

Programmatic loader

For custom integrations, the loader injects the boot script imperatively and is SSR-safe (a no-op on the server):
import { loadPuntego, whenPuntegoReady } from '@puntego/embed/loader';

loadPuntego({ appId: 'gp_your_app_id', apiUrl: 'https://worker.puntego.com' });
const sdk = await whenPuntegoReady();
sdk?.registerTarget('#checkout', { id: 'checkout', description: 'Checkout' });