Plugins
Analytics

Analytics

Adapter-based analytics for Payload v3, with a self-hosted engine, dashboard widgets and per-document stats.

@10x-media/analytics puts web analytics inside the Payload admin. One adapter contract covers a self-hosted native engine and the major providers (GA4, Plausible, Umami, PostHog), so the same view, dashboard widgets and per-document stat fields work wherever the numbers come from, and switching providers is a config change.

payload.config.ts
import { analytics, analyticsDefaultWidgets } from '@10x-media/analytics'
import { native } from '@10x-media/analytics/adapters/native'
import { mongooseAdapter } from '@payloadcms/db-mongodb'
import { buildConfig } from 'payload'

export default buildConfig({
  db: mongooseAdapter({ url: process.env.DATABASE_URL ?? '' }),
  secret: process.env.PAYLOAD_SECRET ?? '',
  admin: {
    dashboard: { widgets: [], defaultLayout: [...analyticsDefaultWidgets()] },
  },
  plugins: [
    analytics({
      adapters: [native()],
      // Required, and your decision: 'off' captures at once, 'all' holds every capture slot
      // until your consent manager calls setGate (see Consent), so it counts nothing without one.
      capture: { gate: 'off' },
      collections: {
        pages: { path: (doc) => (typeof doc.slug === 'string' ? `/${doc.slug}` : null) },
      },
    }),
  ],
})

What it is, and what it is not

It is:

  • An adapter layer. Every surface reads through one adapter contract with a capabilities model: run the native engine, a provider, or several at once, and every surface hides what the active source cannot answer.
  • A self-hosted engine. The native engine stores analytics in your own Payload database, on MongoDB, Postgres or SQLite: raw hits counted into exact daily totals, 30-minute visits with bounce rate and time per visit, geo resolution, bot filtering, and daily visitor salts deleted within days. It sets no cookie.
  • A capture pipeline. A dependency-free browser tracker, a first-party proxy so a vendor's own SDK loads through your origin (GA4's tag excepted, which loads from Google), goals and revenue from markup, form submissions and server code, and a signed exclusion link that keeps staff out of the numbers.
  • Admin surfaces. The analytics view at /admin/analytics, whose whole state is in its URL; dashboard widgets on Payload's Modular Dashboard; and display fields that put a document's own numbers on your collections. Nothing is injected into your schema.
  • An API. An authenticated query endpoint over any configured source, with cached reads served stale while they refresh, and an opt-in sync tier that copies vendor numbers into a collection.

It is not:

  • A consent manager. The plugin stores no consent state and decides nothing about consent. Whether a site may measure before a visitor answers, and under which category, is the site operator's decision, made in a consent manager such as c15t. What the plugin gives that manager is a gate in front of each capture slot, which it opens and closes with setGate.
  • A tag manager. It boots the tracker of the vendors it has adapters for, and nothing else.
  • A legal assessment. Privacy states what a stored hit holds and how long it is kept; whether an install needs a notice or a legal basis is yours to decide.

When to use it

Use the native engine when you want analytics without a third-party service: no cookies, data in your own database, no account to manage. Use a provider adapter when the numbers already live in GA4, Plausible, Umami or PostHog and you want them next to the content they describe. Mixing works too: several adapters can be registered and chosen per widget or field, and on a multi-tenant install each tenant can bring its own.

Requirements

Payload^3.83.0, with @payloadcms/next and @payloadcms/ui at the same range
Next.js^15.2.9 or ^16.2.6
React19
Node.js22.18 or later
@10x-media/fieldsrequired peer: the providers collection seals its keys with its encrypted fields
Optional peers@google-analytics/data (GA4) and maxmind (MaxMind geo lookup)
Native engineMongoDB 5.0, Postgres 12 or SQLite 3.35 or later, SQLite on a database file; not Cloudflare D1, and not MongoDB-compatible services such as Amazon DocumentDB, Azure Cosmos DB or Firestore

Dashboard widgets use Payload's Modular Dashboard, an experimental Payload feature, so they are marked @beta here too (Stability).

Where to go next

On this page