Skip to content

Destinations

Destinations are where Junction sends your events. Each destination is a plain object with three async functions — init, transform, and send — wired together by the collector at runtime.

DestinationPackageRuntime
Google Analytics 4@junctionjs/destination-ga4Client + Server
Amplitude@junctionjs/destination-amplitudeClient + Server
Meta Pixel + CAPI@junctionjs/destination-metaClient + Server
Plausible@junctionjs/destination-plausibleClient
HTTP (Generic)@junctionjs/destination-httpServer

Every destination is registered on the collector via its config:

import { createCollector } from "@junctionjs/core";
import { ga4 } from "@junctionjs/destination-ga4";
const collector = createCollector({
destinations: [
{
destination: ga4,
config: {
measurementId: "G-XXXXXXXXXX",
},
},
],
});

Destinations are factory functions or pre-built objects — never classes. This keeps them tree-shakeable and easy to test.

All destinations use a 3-tier fallback to resolve event names:

  1. Config overrideconfig.eventNameMap["product:viewed"] takes priority
  2. Default map — each destination ships with sensible defaults for common events
  3. Generated name — if no mapping exists, the name is derived from entity_action (or a destination-specific format)
// Example: override a single event name for GA4
{
destination: ga4,
config: {
measurementId: "G-XXXXXXXXXX",
eventNameMap: {
"product:viewed": "my_custom_view_item",
},
},
}
Junction EventGA4AmplitudeMeta
page:viewedpage_viewPage ViewedPageView
product:viewedview_itemProduct ViewedViewContent
product:addedadd_to_cartProduct AddedAddToCart
order:completedpurchaseOrder CompletedPurchase

The _system entity is reserved for Junction’s internal lifecycle events (e.g., _system:initialized). All destinations must return null from transform() for system events — they are never forwarded to external providers.

Junction destinations implement the Destination interface:

interface Destination<TConfig> {
name: string;
version: string;
consent: ConsentCategory[];
runtime: "client" | "server" | "both";
init: (config: TConfig) => Promise<void> | void;
transform: (event: JctEvent, config: TConfig) => unknown | null;
send: (payload: unknown, config: TConfig) => Promise<void>;
onConsent?: (state: ConsentState) => void;
teardown?: () => Promise<void> | void;
}

See the Contributing Guide for the full walkthrough on building a new destination.