@jwiedeman/gtm-kit-react
v1.1.6
Published
React hooks and provider for GTM Kit - Google Tag Manager integration. Supports React 16.8+.
Maintainers
Readme
@jwiedeman/gtm-kit-react
React hooks and provider for Google Tag Manager. StrictMode-safe. Zero double-fires.
The modern React adapter for GTM Kit - uses hooks and Context API for clean, idiomatic React code.
Installation
npm install @jwiedeman/gtm-kit @jwiedeman/gtm-kit-reactyarn add @jwiedeman/gtm-kit @jwiedeman/gtm-kit-reactpnpm add @jwiedeman/gtm-kit @jwiedeman/gtm-kit-reactQuick Start
Step 1: Wrap Your App
// App.tsx or index.tsx
import { GtmProvider } from '@jwiedeman/gtm-kit-react';
function App() {
return (
<GtmProvider config={{ containers: 'GTM-XXXXXX' }}>
<YourApp />
</GtmProvider>
);
}Step 2: Push Events
import { useGtmPush } from '@jwiedeman/gtm-kit-react';
function BuyButton() {
const push = useGtmPush();
const handleClick = () => {
push({ event: 'purchase', value: 49.99 });
};
return <button onClick={handleClick}>Buy Now</button>;
}That's it! GTM is now running.
Features
| Feature | Description | | ------------------- | ----------------------------------------- | | StrictMode-Safe | No double-fires in React development mode | | Hooks-Based | Modern React patterns with Context API | | React 16.8+ | Works with any modern React version | | TypeScript | Full type definitions included | | Consent Mode v2 | Built-in GDPR compliance hooks | | SSR Compatible | Safe for Next.js, Remix, etc. |
Available Hooks
useGtmPush()
Get a push function to send events to GTM.
const push = useGtmPush();
push({ event: 'button_click', button_id: 'cta-main' });useGtm()
Get the full GTM context with all methods.
const { client, push, updateConsent, setConsentDefaults } = useGtm();useGtmConsent()
Manage consent state.
const { updateConsent, setConsentDefaults } = useGtmConsent();
// After user accepts cookies
updateConsent({
ad_storage: 'granted',
analytics_storage: 'granted'
});useGtmClient()
Get the raw GTM client instance.
const client = useGtmClient();useGtmReady()
Get a function that resolves when GTM is fully loaded.
const whenReady = useGtmReady();
useEffect(() => {
whenReady().then(() => {
console.log('GTM is ready!');
});
}, [whenReady]);Consent Mode v2 (GDPR)
Setting Consent Defaults Before GTM Loads
To set consent defaults before GTM initializes, use useGtmConsent in a component that renders early:
import { GtmProvider, useGtmConsent } from '@jwiedeman/gtm-kit-react';
import { consentPresets } from '@jwiedeman/gtm-kit';
// Component that sets consent defaults on mount
function ConsentInitializer({ children }) {
const { setConsentDefaults } = useGtmConsent();
useEffect(() => {
setConsentDefaults(consentPresets.eeaDefault, { region: ['EEA'] });
}, [setConsentDefaults]);
return <>{children}</>;
}
// App wrapper
function App() {
return (
<GtmProvider config={{ containers: 'GTM-XXXXXX' }}>
<ConsentInitializer>
<YourApp />
</ConsentInitializer>
</GtmProvider>
);
}Cookie Banner Component
import { useGtmConsent } from '@jwiedeman/gtm-kit-react';
import { consentPresets } from '@jwiedeman/gtm-kit';
function CookieBanner() {
const { updateConsent } = useGtmConsent();
// Accept all tracking
const acceptAll = () => updateConsent(consentPresets.allGranted);
// Reject all tracking
const rejectAll = () => updateConsent(consentPresets.eeaDefault);
// Analytics only (mixed consent)
const analyticsOnly = () => updateConsent(consentPresets.analyticsOnly);
// Granular: update specific categories
const customChoice = () =>
updateConsent({
analytics_storage: 'granted',
ad_storage: 'denied',
ad_user_data: 'denied',
ad_personalization: 'denied'
});
return (
<div>
<button onClick={acceptAll}>Accept All</button>
<button onClick={rejectAll}>Reject All</button>
<button onClick={analyticsOnly}>Analytics Only</button>
</div>
);
}Partial Updates - Only update what changed:
// User later opts into ads from preference center
updateConsent({ ad_storage: 'granted', ad_user_data: 'granted' });
// Other categories (analytics_storage, ad_personalization) unchangedProvider Options
<GtmProvider
config={{
containers: 'GTM-XXXXXX', // Required
dataLayerName: 'dataLayer', // Optional
host: 'https://custom.host.com', // Optional
scriptAttributes: { nonce: '...' } // Optional: CSP
}}
>
{children}
</GtmProvider>React Router Integration
import { useLocation } from 'react-router-dom';
import { useGtmPush } from '@jwiedeman/gtm-kit-react';
import { useEffect, useRef } from 'react';
function PageTracker() {
const location = useLocation();
const push = useGtmPush();
const lastPath = useRef('');
useEffect(() => {
const path = location.pathname + location.search;
if (path !== lastPath.current) {
lastPath.current = path;
push({ event: 'page_view', page_path: path });
}
}, [location, push]);
return null;
}
// Add to your app
function App() {
return (
<GtmProvider config={{ containers: 'GTM-XXXXXX' }}>
<BrowserRouter>
<PageTracker />
<Routes>...</Routes>
</BrowserRouter>
</GtmProvider>
);
}Why StrictMode-Safe Matters
In React development mode with StrictMode, components mount twice. This causes most GTM libraries to fire events twice. GTM Kit handles this automatically - you get exactly one initialization and no duplicate events.
Requirements
- React 16.8+ (hooks support)
@jwiedeman/gtm-kit(peer dependency)
Related Packages
- Core: @jwiedeman/gtm-kit (required)
- Legacy React: @jwiedeman/gtm-kit-react-legacy (for class components)
- Next.js: @jwiedeman/gtm-kit-next
Support
Have a question, found a bug, or need help?
Open an issue on GitHub — we're actively maintaining this project and respond quickly.
License
MIT
