Destinations

Tracking events are the SDK's own contract: storefront-style names, and the SDK's own objects as data. A destination takes them and remaps them to its vendor's format. Four are built in: the SDK sends to them itself, each from a chunk that loads only when it is on. Any other vendor is a few lines of page script on next.analytics.on. Each event's page shows what every destination gets for it.

DestinationBuilt inOn by defaultConfig keyLoadsVendor docs
GTMYesYesdestinations.gtmNothing moreElevar data layer
GA4YesNodestinations.ga4ga4-*.js, about 2 KBGA4 events
Meta pixelYesNodestinations.metameta-*.js, about 2 KBMeta Pixel standard events
RudderStackYesNodestinations.rudderstackrudderstack-*.js, about 3 KBRudderStack ecommerce spec
Any other vendorNoA listener on the pageNothing

The SDK never loads a vendor's tag: GTM, gtag, the Meta pixel's base code and RudderStack's JavaScript SDK snippet are the page's own. A destination that is on without its tag logs one warning.

The checkout_* events carry the Checkout and checkout_completed the Order as next.on does, with the shopper's user and addresses. GTM, GA4 and Meta leave them out; RudderStack sends the identify() 0.4.x sent, with the email, name, phone and address; a listener picks what its vendor gets.

Config

CODE
<script>
  window.nextConfig = {
    analytics: {
      destinations: {
        gtm: { enabled: true },
        ga4: { enabled: true },
        meta: { enabled: true, storeName: 'shop' },
        rudderstack: { enabled: true },
      },
    },
  };
</script>
SettingEffect
analytics.enabled: falseNo analytics at all: no chunk loads and next.analytics.on receives nothing
destinations.gtm.enabled: falseNothing on window.dataLayer; pushes still reach window.NextDataLayer
destinations.ga4.enabled: trueTracking events go to gtag
destinations.meta.enabled: trueTracking events go to fbq; storeName sets an order's Purchase eventID
destinations.rudderstack.enabled: trueTracking events go to rudderanalytics, as 0.4.x sent them

The 0.4.x keys still work under destinations, which wins when both are set:

0.4.xReads as
analytics.providers.gtm.enableddestinations.gtm.enabled
analytics.providers.facebook.enableddestinations.meta.enabled
nextConfig.storeNamedestinations.meta.storeName
analytics.providers.rudderstack.enableddestinations.rudderstack.enabled
analytics.providers.customNothing: the SDK logs a warning. Send from a listener

By hand

A custom endpoint is not built in. A listener sends each tracking event to it:

CODE
next.analytics.on('*', (event) => {
  fetch('https://collect.example.com/events', { method: 'POST', keepalive: true, body: JSON.stringify(event) });
});

A listener also remaps the tracking events to any other vendor's format:

CODE
next.analytics.on('*', ({ event_type, event_id, data }) => {
  if (event_type === 'page_viewed') return;
  const lines = Array.isArray(data) ? data : (data.lines ?? data.cart?.lines ?? [data]);
  vendor.track(event_type, { id: event_id, value: data.total ?? data.cart?.total, items: lines });
});

It can go anywhere on the page once window.next exists: next.analytics.on first replays the events the page already sent, so a script that runs late misses nothing. A script that can run before the SDK has set window.next queues itself in window.nextReady (see the JavaScript API):

CODE
(window.nextReady ||= []).push((next) => next.analytics.on('*', (event) => vendor.track(event.event_type, event.data)));

Checking it

Open the page with ?debug=1: the debug panel's Analytics tab lists each event this page sent, with its data, and each destination it reached.