kenzi-web-marketing-analytics
v1.0.0
Published
Lightweight web marketing analytics wrapper for Angular, React, and TypeScript browser apps
Downloads
37
Maintainers
Readme
kenzi-web-marketing-analytics
Lightweight marketing analytics for Angular, React, and TypeScript browser apps.
Developed by Kenzi.ai.
kenzi-web-marketing-analytics exposes one TypeScript API and routes events to supported web marketing providers:
- Google Analytics 4 through
gtag - Google Ads conversion tracking through
gtag - Meta Pixel through
fbq - Custom HTTP endpoint tracking
This package is web/browser only. It is not a Capacitor plugin, mobile SDK, native Firebase SDK wrapper, Meta native SDK wrapper, or iOS/Android package. It can be imported safely in SSR builds because browser-only work is guarded until runtime initialization.
Compatibility
- Angular standalone apps
- Angular SSR-safe apps
- React apps
- React SSR frameworks when initialized only in the browser
- Regular TypeScript browser apps
- Future Vue wrappers
- Modern browsers with ES2020 support
The runtime has no Angular or React dependency and guards all browser globals, so importing it during SSR does not access window, document, or localStorage.
Provider scripts are injected by default in the browser:
- Google: injects
https://www.googletagmanager.com/gtag/js?id=<measurementId> - Meta: injects
https://connect.facebook.net/en_US/fbevents.js
Set autoInjectScript: false per provider only when the host app already loads that provider script through Google Tag Manager, a consent manager, or manually managed tags.
Install
npm install kenzi-web-marketing-analyticsLocal package inside this repo:
npm install ./packages/kenzi-web-marketing-analyticsUsage
import {
KenziWebMarketingAnalytics,
KENZI_MARKETING_EVENTS,
} from 'kenzi-web-marketing-analytics';
await KenziWebMarketingAnalytics.initialize({
debug: false, // Enables console diagnostics when true; keep false in production unless troubleshooting.
google: {
enabled: true, // Enables Google Analytics 4 and Google Ads dispatch.
measurementId: 'G-XXXXXXX', // GA4 web stream measurement ID from Google Analytics Admin > Data streams > Web.
adsConversionId: 'AW-XXXXXXX', // Google Ads conversion ID from Google Ads > Goals > Conversions; used to initialize Ads gtag config.
autoInjectScript: true, // Default true; injects gtag.js automatically. Set false only if GTM/consent tooling already loads it.
conversions: {
lead: 'AW-XXXXXXX/LEAD_LABEL', // Google Ads conversion send_to value from the Lead conversion action tag snippet.
purchase: 'AW-XXXXXXX/PURCHASE_LABEL', // Google Ads conversion send_to value from the Purchase conversion action tag snippet.
complete_registration: 'AW-XXXXXXX/SIGNUP_LABEL', // Google Ads conversion send_to value from the Signup conversion action tag snippet.
},
enhancedEcommerce: true, // Keeps GA4 ecommerce params such as items/value/currency for enhanced ecommerce reporting.
},
meta: {
enabled: true, // Enables Meta Pixel dispatch.
pixelId: '123456789', // Pixel ID from Meta Events Manager > Data sources > Pixel settings.
autoInjectScript: true, // Default true; injects fbevents.js automatically. Set false only if the app already loads Meta Pixel.
autoPageView: true, // Sends Meta Pixel PageView on initialization. Keep false if you only want manual/router page views.
},
campaignTracking: {
enabled: true, // Captures UTM params and ad click IDs from the landing URL.
storageKey: 'kenzi_campaign', // localStorage key used to persist campaign attribution.
expirationDays: 30, // Number of days to keep attribution before it expires.
autoAttachToEvents: true, // Default true; attaches stored campaign fields to outgoing events.
},
consent: {
enabled: true, // Sends Google consent defaults through gtag consent mode.
default: {
analyticsStorage: 'granted', // Use granted/denied based on your consent banner's analytics category.
adStorage: 'granted', // Use granted/denied based on your consent banner's advertising cookies category.
adUserData: 'granted', // Use granted/denied based on permission to send user data for ads measurement.
adPersonalization: 'granted', // Use granted/denied based on permission for personalized ads.
},
},
routerTracking: {
enabled: true, // Tracks browser history route changes in SPAs; framework router hooks can still call pageView manually.
},
});
await KenziWebMarketingAnalytics.pageView({
path: '/contact',
title: 'Contact Us',
});
await KenziWebMarketingAnalytics.logEvent({
name: KENZI_MARKETING_EVENTS.LEAD,
params: {
source: 'contact_form',
},
});
await KenziWebMarketingAnalytics.trackPurchase({
amount: 250,
currency: 'AED',
transactionId: 'INV-1001',
});Angular Standalone Setup
Initialize once in browser startup code. In SSR apps, call this only in the browser, or call it universally and rely on the package's browser guards.
import { isPlatformBrowser } from '@angular/common';
import { APP_INITIALIZER, PLATFORM_ID, inject, Provider } from '@angular/core';
import { KenziWebMarketingAnalytics } from 'kenzi-web-marketing-analytics';
export const provideMarketingAnalytics = (): Provider => ({
provide: APP_INITIALIZER,
multi: true,
useFactory: () => {
const platformId = inject(PLATFORM_ID);
return async () => {
if (!isPlatformBrowser(platformId)) {
return;
}
await KenziWebMarketingAnalytics.initialize({
google: {
enabled: true,
measurementId: 'G-XXXXXXX',
},
meta: {
enabled: true,
pixelId: '123456789',
},
});
};
},
});React Setup
This is a framework-neutral TypeScript package, so it works in React as long as initialization runs in the browser.
import { useEffect } from 'react';
import { KenziWebMarketingAnalytics } from 'kenzi-web-marketing-analytics';
export function MarketingAnalyticsBoot() {
useEffect(() => {
void KenziWebMarketingAnalytics.initialize({
google: {
enabled: true,
measurementId: 'G-XXXXXXX',
},
meta: {
enabled: true,
pixelId: '123456789',
},
});
}, []);
return null;
}Track React route/page changes from your router integration:
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
import { KenziWebMarketingAnalytics } from 'kenzi-web-marketing-analytics';
export function AnalyticsRouteTracker() {
const location = useLocation();
useEffect(() => {
void KenziWebMarketingAnalytics.pageView({
path: `${location.pathname}${location.search}`,
title: document.title,
});
}, [location.pathname, location.search]);
return null;
}Track Angular route changes from your app shell or analytics service:
import { NavigationEnd, Router } from '@angular/router';
import { filter } from 'rxjs';
import { KenziWebMarketingAnalytics } from 'kenzi-web-marketing-analytics';
router.events.pipe(filter((event): event is NavigationEnd => event instanceof NavigationEnd)).subscribe((event) => {
void KenziWebMarketingAnalytics.pageView({
path: event.urlAfterRedirects,
title: document.title,
});
});Configuration
await KenziWebMarketingAnalytics.initialize({
google: {
enabled: true,
measurementId: 'G-XXXXXXX',
adsConversionId: 'AW-XXXXXXX',
conversions: {
lead: 'AW-XXXXXXX/LEAD_LABEL',
purchase: 'AW-XXXXXXX/PURCHASE_LABEL',
},
autoInjectScript: true,
autoPageView: false,
sendPageViewOnInitialize: false,
enhancedEcommerce: true,
},
});The package injects the standard gtag.js script by default. autoInjectScript defaults to true; you do not need to pass it for normal browser usage. Set autoInjectScript: false only if the host application already owns script loading through Google Tag Manager, a consent manager, or server-rendered tags.
conversions maps application-level event names to Google Ads conversion labels. When a mapped event is logged, the package sends both the GA4 event and a conversion event with send_to.
autoPageView controls the initial GA4 config page view. It defaults to false because Angular, React, and other SPA apps usually send page views from router changes. sendPageViewOnInitialize remains supported as a backward-compatible alias.
enhancedEcommerce documents that ecommerce params should be sent through this package unchanged for GA4 reports. Pass GA4 ecommerce fields such as items, value, currency, coupon, and transaction_id in params or use trackPurchase.
Meta Pixel
await KenziWebMarketingAnalytics.initialize({
meta: {
enabled: true,
pixelId: '123456789',
autoInjectScript: true,
autoPageView: true,
autoConfig: true,
},
});The package injects Meta Pixel's browser script by default and calls fbq('init', pixelId). autoInjectScript defaults to true; you do not need to pass it for normal browser usage. Set autoInjectScript: false only when the host application already loads Meta Pixel.
Set autoPageView: true if you want an immediate Meta Pixel PageView after initialization. In SPAs with routerTracking or manual pageView calls, keep this false unless you intentionally want the first page view sent during initialization.
Campaign Tracking
await KenziWebMarketingAnalytics.initialize({
campaignTracking: {
enabled: true,
storageKey: 'kenzi_campaign',
expirationDays: 30,
autoAttachToEvents: true,
},
});Campaign tracking captures these URL parameters when present and stores them in localStorage until expiration:
utm_source, utm_medium, utm_campaign, utm_term, utm_content, utm_id, gclid, gbraid, wbraid, fbclid, ttclid, li_fat_id
Stored campaign values are attached to provider event params unless explicitly overridden by event params.
Set autoAttachToEvents: false when you only want campaign data available through getCampaign() or sent to a custom backend manually.
Consent
await KenziWebMarketingAnalytics.initialize({
consent: {
enabled: true,
default: {
analyticsStorage: 'granted',
adStorage: 'granted',
adUserData: 'granted',
adPersonalization: 'granted',
},
},
});Consent defaults are sent to Google through gtag('consent', 'default', ...) before Google provider initialization. Use values from your cookie/consent banner. Supported values are granted and denied.
For stricter markets, start with denied values and update consent from your app after the user accepts. For markets where analytics and ads measurement are already covered by your consent flow before initialization, pass granted values.
Router Tracking
await KenziWebMarketingAnalytics.initialize({
routerTracking: {
enabled: true,
includeHash: false,
},
});Router tracking watches browser history.pushState, history.replaceState, and popstate changes, then sends pageView for SPA navigation. This is framework-neutral and works in Angular, React, Vue, and regular TypeScript browser apps.
Set includeHash: true when hash changes are meaningful routes in your app, such as /docs#getting-started or hash-based routers.
For maximum control over titles, route names, and duplicate prevention, a framework router hook can call pageView manually instead of using routerTracking.
Custom Endpoint
await KenziWebMarketingAnalytics.initialize({
customEndpoint: {
enabled: true,
url: 'https://api.example.com/analytics/events',
headers: {
'x-client': 'website',
},
includeCampaign: true,
},
});The custom endpoint receives a JSON POST payload containing the original event name, mapped provider name, sanitized params, timestamp, and stored campaign data.
Set includeCampaign: false if the endpoint should receive event params only. Advanced tests or custom runtimes can pass fetchImplementation; normal browser apps should use the default window.fetch.
Defaults
| Option | Default | Notes |
| --- | --- | --- |
| debug | false | Enables ***kenzi-marketing*** console diagnostics only when true. |
| google.autoInjectScript | true | Injects gtag.js unless explicitly false. |
| google.autoPageView | false | Best for SPAs; use router/manual page views instead. |
| google.sendPageViewOnInitialize | false | Backward-compatible alias for initial GA4 page view behavior. |
| google.enhancedEcommerce | true | Ecommerce params are preserved. |
| meta.autoInjectScript | true | Injects Meta Pixel fbevents.js unless explicitly false. |
| meta.autoPageView | false | Sends initial Meta PageView only when true. |
| meta.autoConfig | Meta default | Set false to call fbq('set', 'autoConfig', false, pixelId). |
| campaignTracking.storageKey | kenzi_campaign | localStorage attribution key. |
| campaignTracking.expirationDays | 30 | Attribution lifetime. |
| campaignTracking.autoAttachToEvents | true | Adds stored campaign params to outgoing events. |
| routerTracking.includeHash | false | Hash is included only when explicitly true. |
| customEndpoint.includeCampaign | true | Campaign object is included in endpoint payload unless false. |
Event Naming Strategy
Use one application-level event contract in website code, then let this package translate that event to the closest provider-specific name.
Do not scatter GA4, Google Ads, or Meta Pixel event names across product code. Providers do not always use the same standard names. For example:
- App/business event:
complete_registration - GA4 recommended event:
sign_up - Meta Pixel standard event:
CompleteRegistration
The preferred pattern is:
await KenziWebMarketingAnalytics.logEvent({
name: 'complete_registration',
params: {
method: 'email',
},
});Event Name Autocomplete
The package exports readable event constants for TypeScript autocomplete:
import {
KENZI_MARKETING_EVENTS,
KENZI_STANDARD_MARKETING_EVENTS,
type StandardMarketingEventName,
} from 'kenzi-web-marketing-analytics';Use KENZI_MARKETING_EVENTS for dot-autocomplete:
await KenziWebMarketingAnalytics.logEvent({
name: KENZI_MARKETING_EVENTS.LEAD,
});Use KENZI_STANDARD_MARKETING_EVENTS when you need the full list of event string values for iteration or validation.
By default, logEvent maps known standard business events to provider standard events where a standard equivalent exists. To bypass provider mapping:
await KenziWebMarketingAnalytics.logEvent({
name: 'internal_experiment_seen',
params: {
variant: 'A',
},
useProviderStandardEvents: false,
});Route an operation to specific providers:
await KenziWebMarketingAnalytics.logEvent({
name: 'campaign_landing_view',
params: {
campaign: 'ramadan_2026',
},
providers: ['google'],
});Provider Mappings
| App event | Google/GA4 event | Meta Pixel event | Recommended params example |
| --- | --- | --- | --- |
| sign_up, complete_registration | sign_up | CompleteRegistration | { method: 'email' } |
| login | login | login | { method: 'email' } |
| page_view | page_view | PageView | { page_title: 'Contact Us', page_location: '/contact' } |
| screen_view | screen_view | PageView | { screen_name: 'home' } |
| search | search | Search | { search_term: 'apartments' } |
| view_content | select_content | ViewContent | { content_type: 'service', item_id: 'seo' } |
| view_item | view_item | ViewContent | { item_id: 'sku_123', item_name: 'Plan', value: 250, currency: 'AED' } |
| add_to_cart | add_to_cart | AddToCart | { item_id: 'sku_123', value: 250, currency: 'AED' } |
| begin_checkout | begin_checkout | InitiateCheckout | { value: 250, currency: 'AED' } |
| purchase | purchase | Purchase | Prefer trackPurchase({ amount, currency }) |
| lead, generate_lead | generate_lead | Lead | { source: 'contact_form' } |
| contact | contact | Contact | { method: 'whatsapp' } |
| schedule | schedule | Schedule | { service: 'demo' } |
| subscribe | subscribe | Subscribe | { plan: 'monthly', value: 99, currency: 'AED' } |
| start_trial | start_trial | StartTrial | { plan: 'pro', trial_days: 14 } |
Project-specific funnel events should still use logEvent. Keep event names lowercase snake_case, stable, and report-friendly. Avoid placing user-identifying data, product names, order IDs, phone numbers, emails, timestamps, or free text in event names.
API
initialize(options?)
Initializes configured providers and returns enabled provider names.
await KenziWebMarketingAnalytics.initialize(options);pageView(options?)
Tracks a web page view. Defaults to the current browser URL and document title when available.
await KenziWebMarketingAnalytics.pageView({
path: '/contact',
title: 'Contact Us',
location: 'https://example.com/contact',
referrer: 'https://google.com',
params: {
language: 'en',
},
providers: ['google', 'meta'],
});providers can route the page view to selected enabled providers. If omitted, all enabled providers receive it.
logEvent(options)
Tracks a standard or custom marketing event.
await KenziWebMarketingAnalytics.logEvent({
name: 'lead',
params: {
source: 'contact_form',
},
providers: ['google', 'meta', 'customEndpoint'],
useProviderStandardEvents: true,
});useProviderStandardEvents defaults to true. Set it to false only when you want the exact raw event name sent to providers.
trackPurchase(options)
Tracks a purchase with validated amount and ISO 4217 currency code.
await KenziWebMarketingAnalytics.trackPurchase({
amount: 250,
currency: 'AED',
transactionId: 'INV-1001',
params: {
item_id: 'plan_pro',
item_name: 'Pro Plan',
},
providers: ['google', 'meta'],
});setUserId(options)
Sets the Google user ID where supported. Meta Pixel and custom endpoint providers ignore this call.
await KenziWebMarketingAnalytics.setUserId({
userId: '12345',
});setUserProperty(options)
Sets a Google user property where supported.
await KenziWebMarketingAnalytics.setUserProperty({
key: 'language',
value: 'en',
});getCampaign()
Returns the currently stored campaign, if available and not expired.
const campaign = KenziWebMarketingAnalytics.getCampaign();clearCampaign()
Clears stored campaign attribution.
KenziWebMarketingAnalytics.clearCampaign();Development
npm install
npm run verify