Atomic Payload
Plugins

@pro-laico/tracking

Turn analytics on and off from the Payload admin: flip a switch for PostHog, Google Tag Manager, or Vercel Analytics and the right scripts load on your site.

@pro-laico/tracking lets you manage analytics from the Payload admin instead of your environment. Toggle PostHog, Google Tag Manager, or Vercel Analytics on, paste in the keys right in the dashboard, and a provider you add once at your app root loads exactly the tools you've turned on.

Installation

pnpm add @pro-laico/tracking
npm install @pro-laico/tracking
yarn add @pro-laico/tracking

payload, next, and react are peers you already have in a Payload + Next.js app. The analytics SDKs are optional peers, so install only the ones you turn on: posthog-js for PostHog, @next/third-parties for Google Tag Manager, @vercel/analytics for Vercel Analytics.

Setup

@pro-laico/core comes bundled with this plugin — it's a dependency, not a separate install. Its cached reads reach Payload through a config you register once in your Next.js instrumentation hook (registerPayloadConfig); see @pro-laico/core → Setup to wire it up.

Adding analytics to your own Payload + Next.js project.

Add the plugin to your Payload config

import { buildConfig } from 'payload'
import { trackingPlugin } from '@pro-laico/tracking'

export default buildConfig({
  plugins: [trackingPlugin()],
})

This registers the Tracking global, where you turn each analytics tool on and enter its keys.

Turn on the tools you want in the admin

Open the Tracking global (under the Tracking admin group). Each tool has its own toggle in the sidebar; flipping one on reveals its settings:

  • PostHog: enter the project public key and host (defaults to https://us.i.posthog.com). Autocapture is on by default; you can narrow it with optional URL allow / ignore lists.
  • Google Tag Manager: enter your container ID (GTM-XXXXXXX).
  • Vercel Analytics: just the toggle; it has no keys.

Each tool's keys are required only when its toggle is on, so you can save the global with anything you're not using turned off.

Add the provider to your app root

The keys live in the Tracking global, so nothing loads until you fetch that global on the server and hand it to TrackingProvider. Read it with the cached getter (getCachedTracking) so the page reads it once per request, and wrap your frontend layout with the provider. It reads the toggles and renders only the providers you enabled (and renders nothing at all if tracking is missing):

// app/(frontend)/layout.tsx (a Server Component)
import { draftMode } from 'next/headers'

import { getCachedTracking } from '@pro-laico/tracking/cache'
import { TrackingProvider } from '@pro-laico/tracking/provider'

export default async function RootLayout({ children }: { children: React.ReactNode }) {
  const { isEnabled: draft } = await draftMode()
  const tracking = await getCachedTracking(draft)

  return (
    <html lang="en">
      <body>
        <TrackingProvider tracking={tracking}>{children}</TrackingProvider>
      </body>
    </html>
  )
}

TrackingProvider is a client component that pulls in the optional SDKs, which is why it lives behind the /provider subpath: server tooling never has to load posthog-js or the others. You fetch the global on the server and thread it down as a prop.

Register your Payload config

getCachedTracking reaches Payload's Local API through the config you register once in your instrumentation hook (see the callout above and @pro-laico/core → Setup). With that in place, saving the Tracking global revalidates the tracking tag, so a change in the admin flows through to the next page view.

The atomic-payload template already includes the plugin and wires TrackingProvider into the frontend layout (reading the Tracking global through its cached getter). You just choose what to enable.

Turn on the tools you want

Open the Tracking global in the admin. Flip on PostHog, Google Tag Manager, or Vercel Analytics, and fill in the keys each one asks for: the PostHog public key and host, or your Google Tag Manager ID.

Save and reload

The layout reads the global on each request, so once you save your changes the enabled tools load on the next page view. No build step or environment variables to set.

PostHog initialises once on the first non-admin page view and relies on autocapture for events: it records pageviews and interactions automatically. (The earlier custom-property collection and field were removed; the only PostHog tuning is the optional URL allow / ignore lists for autocapture.)

Caching & revalidation

The read this package ships, getCachedTracking(draft) from @pro-laico/tracking/cache, returns the Tracking global so your layout reads the toggles and keys once instead of re-querying Payload on every render. Saving the global in the admin revalidates the tracking tag, so the next page view loads exactly the tools you just changed.

This is the getter the template's layout uses to feed TrackingProvider (the standalone Setup above shows the same wiring). See Caching & revalidation for how the tags and withCache work.

Options

trackingPlugin(options?) accepts:

Prop

Type

Both options are flat booleans, so there are no nested keys to expand. All options at their defaults, as a working starting point:

trackingPlugin({
  enabled: true,
  includeTrackingGlobal: true,
})

Environment variables

This plugin reads no environment variables. Every key (the PostHog public key and host, the Google Tag Manager ID) is entered and stored in the Tracking global in the admin, so the same build works across deployments without juggling per-environment keys.

Exports

Everything @pro-laico/tracking exposes, grouped by where you reach for it.

The plugin

Export

Type

trackingPluginplugin
The plugin itself. Registers the Tracking global where you toggle each tool and enter its keys. Default and named export.

Parameters

options?:TrackingPluginOptionsSee the Options table above. Both keys are optional and default to true.

Returns

PluginA Payload config plugin you add to buildConfig({ plugins: [...] }).

Example

import { buildConfig } from 'payload'import { trackingPlugin } from '@pro-laico/tracking'export default buildConfig({plugins: [trackingPlugin()],})

Location

@pro-laico/tracking
TrackingPluginOptionstype
The TypeScript type for the plugin's options.

Location

@pro-laico/tracking

Frontend

Export

Type

TrackingProviderfunction
The composite client provider for your app root. Reads the Tracking global and renders only the tools you enabled, nesting each enabled provider around children. Renders just children when tracking is missing. Client component.

Parameters

tracking:Tracking | undefinedThe Tracking global, fetched on the server (with getCachedTracking). Its toggles decide which providers render.
children:ReactNodeYour app tree, wrapped by every enabled provider.

Returns

JSX.ElementThe children wrapped in the enabled analytics providers.

Example

import { getCachedTracking } from '@pro-laico/tracking/cache'import { TrackingProvider } from '@pro-laico/tracking/provider'// app/(frontend)/layout.tsxexport default async function RootLayout({ children }) {const tracking = await getCachedTracking(false)return (  <html lang="en">    <body>      <TrackingProvider tracking={tracking}>{children}</TrackingProvider>    </body>  </html>)}

Location

@pro-laico/tracking/provider
PostHogProviderfunction
The PostHog-only client provider, in case you wire providers individually. Initialises PostHog once on the first non-admin view using the key, host, and autocapture settings from the Tracking global.

Parameters

tracking:Tracking | undefinedSupplies postHogPublicKey, postHogHost, and the optional autocapture allow / ignore lists.
children:ReactNodeYour app tree, rendered inside PostHog provider.

Returns

JSX.ElementThe children wrapped in PostHog provider.

Example

import { getCachedTracking } from '@pro-laico/tracking/cache'import { PostHogProvider } from '@pro-laico/tracking/provider'// only the PostHog provider, wired by hand in a layoutconst tracking = await getCachedTracking(false)return <PostHogProvider tracking={tracking}>{children}</PostHogProvider>

Location

@pro-laico/tracking/provider
GoogleTagManagerProviderfunction
The Google Tag Manager-only client provider. Renders the GTM script when tracking.googleTagManagerId is set.

Parameters

tracking:Tracking | undefinedSupplies googleTagManagerId (the GTM-XXXXXXX container ID).
children:ReactNodeYour app tree, rendered alongside the GTM script.

Returns

JSX.ElementThe children plus the GTM script.

Example

import { getCachedTracking } from '@pro-laico/tracking/cache'import { GoogleTagManagerProvider } from '@pro-laico/tracking/provider'const tracking = await getCachedTracking(false)return <GoogleTagManagerProvider tracking={tracking}>{children}</GoogleTagManagerProvider>

Location

@pro-laico/tracking/provider
VercelProviderfunction
The Vercel Analytics-only client provider. Renders the <Analytics /> component alongside your tree; it needs no keys.

Parameters

children:ReactNodeYour app tree, rendered with the Vercel <Analytics /> component.

Returns

JSX.ElementThe children plus the Vercel <Analytics /> component.

Example

import { VercelProvider } from '@pro-laico/tracking/provider'// Vercel Analytics takes no keys, so it needs no tracking propreturn <VercelProvider>{children}</VercelProvider>

Location

@pro-laico/tracking/provider

Cache getters

Export

Type

getCachedTrackingfunction
Cached read of the Tracking global. Wrapped in withCache under the tracking tag and revalidated when you save the global, so the layout reads it once per request instead of re-querying Payload on every render. This is what you pass to TrackingProvider.

Parameters

draft:booleanRead the draft (true) or published (false) variant of the global.

Returns

Promise<Tracking | undefined>The Tracking global, or undefined if it has never been saved.

Example

import { draftMode } from 'next/headers'import { getCachedTracking } from '@pro-laico/tracking/cache'import { TrackingProvider } from '@pro-laico/tracking/provider'// app/(frontend)/layout.tsxexport default async function RootLayout({ children }) {const { isEnabled: draft } = await draftMode()const tracking = await getCachedTracking(draft)return <TrackingProvider tracking={tracking}>{children}</TrackingProvider>}

Location

@pro-laico/tracking/cache

Types

Export

Type

Tracking (type)type
The TypeScript type for the Tracking global, for typed access to your tracking settings.

Location

@pro-laico/tracking/schema

On this page