MenuNext.js

Installation guides

Next.js

Add ProductLense to a Next.js app with next/script in the root layout, then identify signed-in users from a client component.

In Next.js, load ProductLense with the next/script component and strategy="afterInteractive". Put it in the root layout (App Router) or in _app.tsx (Pages Router) so it runs on every page. Client-side navigation is recorded through the history API, so no router hooks are needed.

Add the script

Replace SITE_ID with the id from Settings → Sites in the ProductLense dashboard.

app/layout.tsx

import Script from 'next/script';
import type { ReactNode } from 'react';

export default function RootLayout({ children }: { children: ReactNode }) {
  return (
    <html lang="en">
      <body>
        {children}
        <Script
          src="https://api.productlense.app/a.js"
          data-site="SITE_ID"
          strategy="afterInteractive"
        />
      </body>
    </html>
  );
}

pages/_app.tsx

import Script from 'next/script';
import type { AppProps } from 'next/app';

export default function App({ Component, pageProps }: AppProps) {
  return (
    <>
      <Script
        src="https://api.productlense.app/a.js"
        data-site="SITE_ID"
        strategy="afterInteractive"
      />
      <Component {...pageProps} />
    </>
  );
}

Do not use strategy="worker": the script must run on the main thread, where it reads its own tag through document.currentScript.

Identify users

Render a small client component wherever the signed-in user is known, for example in a layout that loads the session. It uses optional chaining so nothing fails if the script has not loaded yet or is blocked.

app/productlense-identify.tsx
'use client';
import { useEffect } from 'react';

declare global {
  interface Window {
    productlense?: (...args: unknown[]) => void;
  }
}

export function ProductLenseIdentify({ userId }: { userId?: string }) {
  useEffect(() => {
    if (userId) window.productlense?.('identify', userId);
  }, [userId]);
  return null;
}
app/(app)/layout.tsx
<ProductLenseIdentify userId={session.user.id} />

Use the same id you store on the customer in your billing provider; see how payments are attributed.

Custom events

TSX
window.productlense?.('event', 'Upgrade clicked', { plan: 'growth' });

afterInteractive loads the script after hydration, so an event fired during the first render may arrive before window.productlense exists. Call events from handlers and effects, or add the queue stub with strategy="beforeInteractive" in the root layout if you need calls to be queued:

app/layout.tsx
<Script id="productlense-stub" strategy="beforeInteractive">
  {`window.productlense=window.productlense||function(){(window.productlense.q=window.productlense.q||[]).push(arguments)}`}
</Script>

See custom events for naming rules.

Verify your installation

  1. Deploy, then open your site in a normal browser window.
  2. Open the Live page in the ProductLense dashboard. Your visit appears within a few seconds.
  3. In the browser's developer tools, the Network tab shows a POST to https://api.productlense.app/api/e with status 204. Click a link to another page: a second pageview follows.

next dev runs on localhost, where nothing is sent unless you add data-dev="true" to the Script component. Ad blockers in your own browser may block the request. If the visit does not appear, follow Verify your installation.

    ↑ ↓ to move, Enter to open