npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@adkit/google-ads-tracking

v1.1.2

Published

Google Ads conversion tracking for JavaScript and TypeScript. Zero-dependency gtag.js wrapper with eager, lazy, and manual script loading.

Readme

Google Ads Conversion Tracking for JavaScript & TypeScript

Track Google Ads conversions from any JavaScript or TypeScript app. A zero-dependency gtag.js wrapper, 1.2 KB gzipped, with eager, lazy, and manual script loading. Conversions fired before gtag.js loads are queued, not lost.

npm version npm downloads License: MIT

Works with React, Next.js, Vue, Nuxt, Svelte, and plain JavaScript. Written in TypeScript, ships its own type definitions.

Install

npm install @adkit/google-ads-tracking

Quick start

import GOOGLE from '@adkit/google-ads-tracking';

// 1. Initialize once, in your app entry point
GOOGLE.init({ tagId: 'AW-XXXXXXXXXX' });

// 2. Track a conversion anywhere in your app
GOOGLE.trackConversion('AW-XXXXXXXXXX/CONVERSION_LABEL', {
    value: 29.99,
    currency: 'USD',
});

init() installs Google's command queue and loads gtag.js from googletagmanager.com. trackConversion() sends a conversion event to Google Ads with an optional value, currency, and transaction ID.

Why not paste the gtag snippet?

Google's copy-paste snippet works, but it gives you a blocking script in <head>, no types, and no control over when gtag.js loads. This wrapper fixes the parts that hurt in a real app:

  • No lost conversions. The command queue is installed synchronously during init(), in every loading mode. Conversions tracked before gtag.js arrives sit in the queue and are sent once it loads.
  • Control when the script loads. Load gtag.js immediately, after the page is idle, or only when you decide. See script loading modes.
  • Typed API. GoogleTrackingConfig and ConversionParams are exported, so wrong parameters fail at compile time instead of silently dropping data.
  • Safe defaults. Tracking is off on localhost by default, and init() is a no-op during server-side rendering, so it won't crash SSR frameworks.
  • Debug mode. Color-coded console logs show every init and conversion call during development.
  • Small. 1.2 KB gzipped, zero dependencies.

Script loading modes

loadMode controls when the external gtag.js script is requested. The command queue is installed immediately in all three modes, so trackConversion() works the same everywhere.

| Mode | When gtag.js loads | Use when | | -------- | ----------------------------------------------------------- | ---------------------------------------------------- | | eager | During init() (default) | You want conversions reported as early as possible | | lazy | After the page load event, at the next idle moment | You're protecting Core Web Vitals / page speed | | manual | Only when you call load() | You gate tracking behind cookie consent or a router |

GOOGLE.init({
    tagId: 'AW-XXXXXXXXXX',
    loadMode: 'manual',
});

// Queued in order, sent once gtag.js loads
GOOGLE.trackConversion('AW-XXXXXXXXXX/CONVERSION_LABEL');

// Safe to call more than once; the script is only requested once
GOOGLE.load();

In lazy mode the script waits for the page load event, then uses requestIdleCallback (with a 2 second timeout, falling back to setTimeout in browsers without idle callbacks). If the script fails to load, queued conversions are kept and load() can be called again.

Configuration

| Option | Type | Default | Description | | ----------------- | ------------------------------- | --------- | ------------------------------------------------------------------ | | tagId | string \| string[] | required | Google tag ID, or several (e.g. 'AW-XXXXXXXXXX') | | loadMode | 'eager' \| 'lazy' \| 'manual' | 'eager' | When to load the external gtag.js script | | debug | boolean | false | Log every init and conversion call to the console | | enableLocalhost | boolean | false | Track on localhost (off by default so dev traffic stays out) |

Multiple Google Ads accounts:

GOOGLE.init({ tagId: ['AW-FIRST_TAG', 'AW-SECOND_TAG'] });

Every tag ID receives a config command. Conversions route by the send_to value you pass to trackConversion().

Tracking conversions

trackConversion(conversionId, params?) takes the full conversion ID (AW-XXXXXXXXXX/CONVERSION_LABEL, from your Google Ads conversion action) and optional parameters:

| Parameter | Type | Description | Example | | ---------------- | -------- | -------------------------------------------------- | ---------------- | | value | number | Monetary value of the conversion | 99.99 | | currency | string | ISO 4217 currency code | 'USD', 'EUR' | | transaction_id | string | Unique ID so Google deduplicates repeat conversions | 'ORDER_12345' |

Custom parameters pass through unchanged.

// E-commerce purchase
GOOGLE.trackConversion('AW-XXXXX/purchase_label', {
    value: 149.99,
    currency: 'USD',
    transaction_id: 'order_abc123',
});

// Lead form submission (no value)
GOOGLE.trackConversion('AW-XXXXX/lead_label');

// Sign-up with a custom parameter
GOOGLE.trackConversion('AW-XXXXX/signup_label', {
    value: 10,
    currency: 'USD',
    new_customer: true,
});

Usage with React and Next.js

Initialize once in your entry point, then call trackConversion() from event handlers.

React (Vite / CRA), in main.tsx:

import GOOGLE from '@adkit/google-ads-tracking';

GOOGLE.init({ tagId: 'AW-XXXXXXXXXX', loadMode: 'lazy' });

Next.js (App Router), in a client component mounted from your root layout:

'use client';
import { useEffect } from 'react';
import GOOGLE from '@adkit/google-ads-tracking';

export function GoogleAdsTracking() {
    useEffect(() => {
        GOOGLE.init({ tagId: 'AW-XXXXXXXXXX', loadMode: 'lazy' });
    }, []);
    return null;
}

init() checks for window and does nothing on the server, so importing it in SSR code is safe.

Usage with Vue and Nuxt

Vue, in main.ts:

import GOOGLE from '@adkit/google-ads-tracking';

GOOGLE.init({ tagId: 'AW-XXXXXXXXXX', loadMode: 'lazy' });

Nuxt, in a client-only plugin (plugins/google-ads.client.ts):

import GOOGLE from '@adkit/google-ads-tracking';

export default defineNuxtPlugin(() => {
    GOOGLE.init({ tagId: 'AW-XXXXXXXXXX', loadMode: 'lazy' });
});

API reference

| Method | Returns | What it does | | ------------------------------------ | ------------------------------ | --------------------------------------------------------------------- | | init(config) | void | Installs the gtag command queue, then loads gtag.js per loadMode | | trackConversion(id, params?) | void | Sends a Google Ads conversion event | | load() | void | Requests gtag.js (for manual mode; safe to call repeatedly) | | isLoaded() | boolean | true once the gtag command queue is installed | | getConfig() | GoogleTrackingConfig \| null | The active configuration |

The default export GOOGLE is a shared singleton. For separate instances (e.g. different tags per app section), use the factory:

import { createGoogleTracking } from '@adkit/google-ads-tracking';

const tracker = createGoogleTracking();
tracker.init({ tagId: 'AW-XXXXXXXXXX' });

TypeScript

Type definitions are bundled, no @types package needed.

import type { GoogleTrackingConfig, ConversionParams, GoogleTrackingLoadMode } from '@adkit/google-ads-tracking';

Debug mode

Pass debug: true to init() to log every step with color-coded [Google Tracking] console messages: initialization, script loading, each conversion with its parameters, and warnings when a conversion is dropped.

GOOGLE.init({
    tagId: 'AW-XXXXXXXXXX',
    debug: true,
    enableLocalhost: true, // also track during local development
});

Migrating from @adkit.so/google-tracking

This package replaces @adkit.so/google-tracking. The API is unchanged; loadMode is new.

npm uninstall @adkit.so/google-tracking
npm install @adkit/google-ads-tracking

Then update the import:

- import GOOGLE from '@adkit.so/google-tracking';
+ import GOOGLE from '@adkit/google-ads-tracking';

The old package keeps working but won't receive new features.

FAQ

Do I need Google Tag Manager?

No. This library loads gtag.js directly, which is Google's recommended path for Google Ads conversion tracking without a tag management layer. If you already run GTM and fire conversions there, you don't need this package.

Can I track a conversion before gtag.js has loaded?

Yes. init() installs Google's command queue synchronously, so conversions are buffered in window.dataLayer in call order and sent when gtag.js processes the queue. This is what makes lazy and manual modes safe.

Why aren't my conversions showing in Google Ads?

Common causes, in order:

  1. Tracking is disabled on localhost by default. Set enableLocalhost: true to test locally.
  2. Wrong ID format. init() takes the tag ID (AW-XXXXXXXXXX), trackConversion() takes tag ID plus label (AW-XXXXXXXXXX/CONVERSION_LABEL).
  3. Google Ads reporting lags. Conversions can take up to 24 hours to appear.
  4. Ad blockers block googletagmanager.com. Test in a clean profile.
  5. The conversion action isn't set up or is inactive in Google Ads.

Turn on debug: true and check the console, then verify the tag with Google Tag Assistant.

Does it work with server-side rendering?

Yes. init() returns early when window is undefined, so it's safe to import and call in Next.js, Nuxt, or any SSR framework. Tracking only runs in the browser.

How do I wait for cookie consent?

Use loadMode: 'manual' and call load() after the user accepts. Conversions tracked in the meantime are queued locally. Note that queued commands live in window.dataLayer, so if your consent policy requires that nothing is prepared before opt-in, call init() itself after consent.

Does it slow down my page?

gtag.js is always loaded async, and the wrapper itself is 1.2 KB gzipped. With loadMode: 'lazy' the script isn't even requested until after the page load event, keeping it out of your Core Web Vitals window.

Google Ads documentation

Related packages

The Meta (Facebook) Pixel family uses the same wrapper approach:

License

MIT


Built by AdKit, the ad management platform for developers and small teams.