@jwiedeman/gtm-kit-react 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +276 -0
- package/dist/index.cjs +16 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +26 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/package.json +58 -0
package/README.md
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
# @react-gtm-kit/react-modern
|
|
2
|
+
|
|
3
|
+
[](https://github.com/jwiedeman/react-gtm-kit/actions/workflows/ci.yml)
|
|
4
|
+
[](https://codecov.io/gh/jwiedeman/react-gtm-kit)
|
|
5
|
+
[](https://www.npmjs.com/package/@react-gtm-kit/react-modern)
|
|
6
|
+
[](https://bundlephobia.com/package/@react-gtm-kit/react-modern)
|
|
7
|
+
[](https://www.typescriptlang.org/)
|
|
8
|
+
[](https://opensource.org/licenses/MIT)
|
|
9
|
+
[](https://reactjs.org/)
|
|
10
|
+
|
|
11
|
+
**React hooks and provider for Google Tag Manager. StrictMode-safe. Zero double-fires.**
|
|
12
|
+
|
|
13
|
+
The modern React adapter for GTM Kit - uses hooks and Context API for clean, idiomatic React code.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @react-gtm-kit/core @react-gtm-kit/react-modern
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
yarn add @react-gtm-kit/core @react-gtm-kit/react-modern
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pnpm add @react-gtm-kit/core @react-gtm-kit/react-modern
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
35
|
+
### Step 1: Wrap Your App
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
// App.tsx or index.tsx
|
|
39
|
+
import { GtmProvider } from '@react-gtm-kit/react-modern';
|
|
40
|
+
|
|
41
|
+
function App() {
|
|
42
|
+
return (
|
|
43
|
+
<GtmProvider config={{ containers: 'GTM-XXXXXX' }}>
|
|
44
|
+
<YourApp />
|
|
45
|
+
</GtmProvider>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Step 2: Push Events
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
import { useGtmPush } from '@react-gtm-kit/react-modern';
|
|
54
|
+
|
|
55
|
+
function BuyButton() {
|
|
56
|
+
const push = useGtmPush();
|
|
57
|
+
|
|
58
|
+
const handleClick = () => {
|
|
59
|
+
push({ event: 'purchase', value: 49.99 });
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
return <button onClick={handleClick}>Buy Now</button>;
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**That's it!** GTM is now running.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Features
|
|
71
|
+
|
|
72
|
+
| Feature | Description |
|
|
73
|
+
| ------------------- | ----------------------------------------- |
|
|
74
|
+
| **StrictMode-Safe** | No double-fires in React development mode |
|
|
75
|
+
| **Hooks-Based** | Modern React patterns with Context API |
|
|
76
|
+
| **React 16.8+** | Works with any modern React version |
|
|
77
|
+
| **TypeScript** | Full type definitions included |
|
|
78
|
+
| **Consent Mode v2** | Built-in GDPR compliance hooks |
|
|
79
|
+
| **SSR Compatible** | Safe for Next.js, Remix, etc. |
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Available Hooks
|
|
84
|
+
|
|
85
|
+
### `useGtmPush()`
|
|
86
|
+
|
|
87
|
+
Get a push function to send events to GTM.
|
|
88
|
+
|
|
89
|
+
```tsx
|
|
90
|
+
const push = useGtmPush();
|
|
91
|
+
|
|
92
|
+
push({ event: 'button_click', button_id: 'cta-main' });
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### `useGtm()`
|
|
96
|
+
|
|
97
|
+
Get the full GTM context with all methods.
|
|
98
|
+
|
|
99
|
+
```tsx
|
|
100
|
+
const { client, push, updateConsent, setConsentDefaults } = useGtm();
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### `useGtmConsent()`
|
|
104
|
+
|
|
105
|
+
Manage consent state.
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
const { updateConsent, setConsentDefaults } = useGtmConsent();
|
|
109
|
+
|
|
110
|
+
// After user accepts cookies
|
|
111
|
+
updateConsent({
|
|
112
|
+
ad_storage: 'granted',
|
|
113
|
+
analytics_storage: 'granted'
|
|
114
|
+
});
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### `useGtmClient()`
|
|
118
|
+
|
|
119
|
+
Get the raw GTM client instance.
|
|
120
|
+
|
|
121
|
+
```tsx
|
|
122
|
+
const client = useGtmClient();
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### `useGtmReady()`
|
|
126
|
+
|
|
127
|
+
Get a function that resolves when GTM is fully loaded.
|
|
128
|
+
|
|
129
|
+
```tsx
|
|
130
|
+
const whenReady = useGtmReady();
|
|
131
|
+
|
|
132
|
+
useEffect(() => {
|
|
133
|
+
whenReady().then(() => {
|
|
134
|
+
console.log('GTM is ready!');
|
|
135
|
+
});
|
|
136
|
+
}, [whenReady]);
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Consent Mode v2 (GDPR)
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
import { GtmProvider, useGtmConsent } from '@react-gtm-kit/react-modern';
|
|
145
|
+
import { consentPresets } from '@react-gtm-kit/core';
|
|
146
|
+
|
|
147
|
+
// Set defaults BEFORE GTM loads
|
|
148
|
+
<GtmProvider
|
|
149
|
+
config={{ containers: 'GTM-XXXXXX' }}
|
|
150
|
+
onBeforeInit={(client) => {
|
|
151
|
+
client.setConsentDefaults(consentPresets.eeaDefault, { region: ['EEA'] });
|
|
152
|
+
}}
|
|
153
|
+
>
|
|
154
|
+
<App />
|
|
155
|
+
</GtmProvider>;
|
|
156
|
+
|
|
157
|
+
// In your cookie banner
|
|
158
|
+
function CookieBanner() {
|
|
159
|
+
const { updateConsent } = useGtmConsent();
|
|
160
|
+
|
|
161
|
+
// Accept all tracking
|
|
162
|
+
const acceptAll = () => updateConsent(consentPresets.allGranted);
|
|
163
|
+
|
|
164
|
+
// Reject all tracking
|
|
165
|
+
const rejectAll = () => updateConsent(consentPresets.eeaDefault);
|
|
166
|
+
|
|
167
|
+
// Analytics only (mixed consent)
|
|
168
|
+
const analyticsOnly = () => updateConsent(consentPresets.analyticsOnly);
|
|
169
|
+
|
|
170
|
+
// Granular: update specific categories
|
|
171
|
+
const customChoice = () =>
|
|
172
|
+
updateConsent({
|
|
173
|
+
analytics_storage: 'granted',
|
|
174
|
+
ad_storage: 'denied',
|
|
175
|
+
ad_user_data: 'denied',
|
|
176
|
+
ad_personalization: 'denied'
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
return (
|
|
180
|
+
<div>
|
|
181
|
+
<button onClick={acceptAll}>Accept All</button>
|
|
182
|
+
<button onClick={rejectAll}>Reject All</button>
|
|
183
|
+
<button onClick={analyticsOnly}>Analytics Only</button>
|
|
184
|
+
</div>
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**Partial Updates** - Only update what changed:
|
|
190
|
+
|
|
191
|
+
```tsx
|
|
192
|
+
// User later opts into ads from preference center
|
|
193
|
+
updateConsent({ ad_storage: 'granted', ad_user_data: 'granted' });
|
|
194
|
+
// Other categories (analytics_storage, ad_personalization) unchanged
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Provider Options
|
|
200
|
+
|
|
201
|
+
```tsx
|
|
202
|
+
<GtmProvider
|
|
203
|
+
config={{
|
|
204
|
+
containers: 'GTM-XXXXXX', // Required
|
|
205
|
+
dataLayerName: 'dataLayer', // Optional
|
|
206
|
+
host: 'https://custom.host.com', // Optional
|
|
207
|
+
scriptAttributes: { nonce: '...' } // Optional: CSP
|
|
208
|
+
}}
|
|
209
|
+
onBeforeInit={(client) => {
|
|
210
|
+
// Called before GTM initializes
|
|
211
|
+
// Perfect for consent defaults
|
|
212
|
+
}}
|
|
213
|
+
onAfterInit={(client) => {
|
|
214
|
+
// Called after GTM initializes
|
|
215
|
+
}}
|
|
216
|
+
>
|
|
217
|
+
{children}
|
|
218
|
+
</GtmProvider>
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## React Router Integration
|
|
224
|
+
|
|
225
|
+
```tsx
|
|
226
|
+
import { useLocation } from 'react-router-dom';
|
|
227
|
+
import { useGtmPush } from '@react-gtm-kit/react-modern';
|
|
228
|
+
import { useEffect, useRef } from 'react';
|
|
229
|
+
|
|
230
|
+
function PageTracker() {
|
|
231
|
+
const location = useLocation();
|
|
232
|
+
const push = useGtmPush();
|
|
233
|
+
const lastPath = useRef('');
|
|
234
|
+
|
|
235
|
+
useEffect(() => {
|
|
236
|
+
const path = location.pathname + location.search;
|
|
237
|
+
if (path !== lastPath.current) {
|
|
238
|
+
lastPath.current = path;
|
|
239
|
+
push({ event: 'page_view', page_path: path });
|
|
240
|
+
}
|
|
241
|
+
}, [location, push]);
|
|
242
|
+
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Add to your app
|
|
247
|
+
function App() {
|
|
248
|
+
return (
|
|
249
|
+
<GtmProvider config={{ containers: 'GTM-XXXXXX' }}>
|
|
250
|
+
<BrowserRouter>
|
|
251
|
+
<PageTracker />
|
|
252
|
+
<Routes>...</Routes>
|
|
253
|
+
</BrowserRouter>
|
|
254
|
+
</GtmProvider>
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## Why StrictMode-Safe Matters
|
|
262
|
+
|
|
263
|
+
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.
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## Requirements
|
|
268
|
+
|
|
269
|
+
- React 16.8+ (hooks support)
|
|
270
|
+
- `@react-gtm-kit/core` (peer dependency)
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## License
|
|
275
|
+
|
|
276
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('react');
|
|
4
|
+
var gtmKit = require('@jwiedeman/gtm-kit');
|
|
5
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
6
|
+
|
|
7
|
+
var u=react.createContext(null),d=(t,n)=>{process.env.NODE_ENV!=="production"&&t!==n&&console.warn("[react-gtm-kit] GtmProvider received new configuration; reconfiguration after mount is not supported. The initial configuration will continue to be used.");},G=t=>{let n=react.useRef(),e=react.useRef();return n.current?e.current&&d(e.current,t):(n.current=gtmKit.createGtmClient(t),e.current=t),n.current},f=({config:t,children:n})=>{let e=G(t);react.useEffect(()=>(e.init(),()=>{e.teardown();}),[e]);let p=react.useMemo(()=>({client:e,push:o=>e.push(o),setConsentDefaults:(o,r)=>e.setConsentDefaults(o,r),updateConsent:(o,r)=>e.updateConsent(o,r),whenReady:()=>e.whenReady(),onReady:o=>e.onReady(o)}),[e]);return jsxRuntime.jsx(u.Provider,{value:p,children:n})},s=()=>{let t=react.useContext(u);if(!t)throw new Error("useGtm hook must be used within a GtmProvider instance.");return t},v=s,x=()=>s().client,y=()=>s().push,h=()=>{let{setConsentDefaults:t,updateConsent:n}=s();return react.useMemo(()=>({setConsentDefaults:t,updateConsent:n}),[t,n])},P=()=>{let{whenReady:t}=s();return t};
|
|
8
|
+
|
|
9
|
+
exports.GtmProvider = f;
|
|
10
|
+
exports.useGtm = v;
|
|
11
|
+
exports.useGtmClient = x;
|
|
12
|
+
exports.useGtmConsent = h;
|
|
13
|
+
exports.useGtmPush = y;
|
|
14
|
+
exports.useGtmReady = P;
|
|
15
|
+
//# sourceMappingURL=out.js.map
|
|
16
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/provider.tsx"],"names":["createContext","useContext","useEffect","useMemo","useRef","createGtmClient","jsx","GtmContext","warnOnConfigChange","initialConfig","nextConfig","useStableClient","config","clientRef","configRef","GtmProvider","children","client","value","state","options","callback","useGtmContext","context","useGtm","useGtmClient","useGtmPush","useGtmConsent","setConsentDefaults","updateConsent","useGtmReady","whenReady"],"mappings":"AAAA,OAAS,iBAAAA,EAAe,cAAAC,EAAY,aAAAC,EAAW,WAAAC,EAAS,UAAAC,MAAgD,QACxG,OACE,mBAAAC,MAOK,qBAmEE,cAAAC,MAAA,oBA/CT,IAAMC,EAAaP,EAAsC,IAAI,EAEvDQ,EAAqB,CAACC,EAAuCC,IAA6C,CAC1G,QAAQ,IAAI,WAAa,cAAgBD,IAAkBC,GAC7D,QAAQ,KACN,2JAEF,CAEJ,EAEMC,EAAmBC,GAA8C,CACrE,IAAMC,EAAYT,EAAkB,EAC9BU,EAAYV,EAA+B,EAEjD,OAAKS,EAAU,QAGJC,EAAU,SACnBN,EAAmBM,EAAU,QAASF,CAAM,GAH5CC,EAAU,QAAUR,EAAgBO,CAAM,EAC1CE,EAAU,QAAUF,GAKfC,EAAU,OACnB,EAEaE,EAAc,CAAC,CAAE,OAAAH,EAAQ,SAAAI,CAAS,IAAqC,CAClF,IAAMC,EAASN,EAAgBC,CAAM,EAErCV,EAAU,KACRe,EAAO,KAAK,EACL,IAAM,CACXA,EAAO,SAAS,CAClB,GACC,CAACA,CAAM,CAAC,EAEX,IAAMC,EAAQf,EACZ,KAAO,CACL,OAAAc,EACA,KAAOC,GAAUD,EAAO,KAAKC,CAAK,EAClC,mBAAoB,CAACC,EAAOC,IAAYH,EAAO,mBAAmBE,EAAOC,CAAO,EAChF,cAAe,CAACD,EAAOC,IAAYH,EAAO,cAAcE,EAAOC,CAAO,EACtE,UAAW,IAAMH,EAAO,UAAU,EAClC,QAAUI,GAAaJ,EAAO,QAAQI,CAAQ,CAChD,GACA,CAACJ,CAAM,CACT,EAEA,OAAOX,EAACC,EAAW,SAAX,CAAoB,MAAOW,EAAQ,SAAAF,EAAS,CACtD,EAEMM,EAAgB,IAAuB,CAC3C,IAAMC,EAAUtB,EAAWM,CAAU,EACrC,GAAI,CAACgB,EACH,MAAM,IAAI,MAAM,yDAAyD,EAE3E,OAAOA,CACT,EAEaC,EAASF,EAETG,EAAe,IACnBH,EAAc,EAAE,OAGZI,EAAa,IACjBJ,EAAc,EAAE,KAGZK,EAAgB,IAAqB,CAChD,GAAM,CAAE,mBAAAC,EAAoB,cAAAC,CAAc,EAAIP,EAAc,EAC5D,OAAOnB,EAAQ,KAAO,CAAE,mBAAAyB,EAAoB,cAAAC,CAAc,GAAI,CAACD,EAAoBC,CAAa,CAAC,CACnG,EAEaC,EAAc,IAA0C,CACnE,GAAM,CAAE,UAAAC,CAAU,EAAIT,EAAc,EACpC,OAAOS,CACT","sourcesContent":["import { createContext, useContext, useEffect, useMemo, useRef, type PropsWithChildren, type JSX } from 'react';\nimport {\n createGtmClient,\n type ConsentRegionOptions,\n type ConsentState,\n type CreateGtmClientOptions,\n type DataLayerValue,\n type GtmClient,\n type ScriptLoadState\n} from '@jwiedeman/gtm-kit';\n\nexport interface GtmProviderProps extends PropsWithChildren {\n config: CreateGtmClientOptions;\n}\n\nexport interface GtmContextValue {\n client: GtmClient;\n push: (value: DataLayerValue) => void;\n setConsentDefaults: (state: ConsentState, options?: ConsentRegionOptions) => void;\n updateConsent: (state: ConsentState, options?: ConsentRegionOptions) => void;\n whenReady: () => Promise<ScriptLoadState[]>;\n onReady: (callback: (state: ScriptLoadState[]) => void) => () => void;\n}\n\nexport interface GtmConsentApi {\n setConsentDefaults: (state: ConsentState, options?: ConsentRegionOptions) => void;\n updateConsent: (state: ConsentState, options?: ConsentRegionOptions) => void;\n}\n\nconst GtmContext = createContext<GtmContextValue | null>(null);\n\nconst warnOnConfigChange = (initialConfig: CreateGtmClientOptions, nextConfig: CreateGtmClientOptions): void => {\n if (process.env.NODE_ENV !== 'production' && initialConfig !== nextConfig) {\n console.warn(\n '[react-gtm-kit] GtmProvider received new configuration; reconfiguration after mount is not supported. ' +\n 'The initial configuration will continue to be used.'\n );\n }\n};\n\nconst useStableClient = (config: CreateGtmClientOptions): GtmClient => {\n const clientRef = useRef<GtmClient>();\n const configRef = useRef<CreateGtmClientOptions>();\n\n if (!clientRef.current) {\n clientRef.current = createGtmClient(config);\n configRef.current = config;\n } else if (configRef.current) {\n warnOnConfigChange(configRef.current, config);\n }\n\n return clientRef.current!;\n};\n\nexport const GtmProvider = ({ config, children }: GtmProviderProps): JSX.Element => {\n const client = useStableClient(config);\n\n useEffect(() => {\n client.init();\n return () => {\n client.teardown();\n };\n }, [client]);\n\n const value = useMemo<GtmContextValue>(\n () => ({\n client,\n push: (value) => client.push(value),\n setConsentDefaults: (state, options) => client.setConsentDefaults(state, options),\n updateConsent: (state, options) => client.updateConsent(state, options),\n whenReady: () => client.whenReady(),\n onReady: (callback) => client.onReady(callback)\n }),\n [client]\n );\n\n return <GtmContext.Provider value={value}>{children}</GtmContext.Provider>;\n};\n\nconst useGtmContext = (): GtmContextValue => {\n const context = useContext(GtmContext);\n if (!context) {\n throw new Error('useGtm hook must be used within a GtmProvider instance.');\n }\n return context;\n};\n\nexport const useGtm = useGtmContext;\n\nexport const useGtmClient = (): GtmClient => {\n return useGtmContext().client;\n};\n\nexport const useGtmPush = (): ((value: DataLayerValue) => void) => {\n return useGtmContext().push;\n};\n\nexport const useGtmConsent = (): GtmConsentApi => {\n const { setConsentDefaults, updateConsent } = useGtmContext();\n return useMemo(() => ({ setConsentDefaults, updateConsent }), [setConsentDefaults, updateConsent]);\n};\n\nexport const useGtmReady = (): (() => Promise<ScriptLoadState[]>) => {\n const { whenReady } = useGtmContext();\n return whenReady;\n};\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { PropsWithChildren, JSX } from 'react';
|
|
2
|
+
import { CreateGtmClientOptions, GtmClient, DataLayerValue, ConsentState, ConsentRegionOptions, ScriptLoadState } from '@jwiedeman/gtm-kit';
|
|
3
|
+
|
|
4
|
+
interface GtmProviderProps extends PropsWithChildren {
|
|
5
|
+
config: CreateGtmClientOptions;
|
|
6
|
+
}
|
|
7
|
+
interface GtmContextValue {
|
|
8
|
+
client: GtmClient;
|
|
9
|
+
push: (value: DataLayerValue) => void;
|
|
10
|
+
setConsentDefaults: (state: ConsentState, options?: ConsentRegionOptions) => void;
|
|
11
|
+
updateConsent: (state: ConsentState, options?: ConsentRegionOptions) => void;
|
|
12
|
+
whenReady: () => Promise<ScriptLoadState[]>;
|
|
13
|
+
onReady: (callback: (state: ScriptLoadState[]) => void) => () => void;
|
|
14
|
+
}
|
|
15
|
+
interface GtmConsentApi {
|
|
16
|
+
setConsentDefaults: (state: ConsentState, options?: ConsentRegionOptions) => void;
|
|
17
|
+
updateConsent: (state: ConsentState, options?: ConsentRegionOptions) => void;
|
|
18
|
+
}
|
|
19
|
+
declare const GtmProvider: ({ config, children }: GtmProviderProps) => JSX.Element;
|
|
20
|
+
declare const useGtm: () => GtmContextValue;
|
|
21
|
+
declare const useGtmClient: () => GtmClient;
|
|
22
|
+
declare const useGtmPush: () => ((value: DataLayerValue) => void);
|
|
23
|
+
declare const useGtmConsent: () => GtmConsentApi;
|
|
24
|
+
declare const useGtmReady: () => (() => Promise<ScriptLoadState[]>);
|
|
25
|
+
|
|
26
|
+
export { GtmConsentApi, GtmContextValue, GtmProvider, GtmProviderProps, useGtm, useGtmClient, useGtmConsent, useGtmPush, useGtmReady };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { PropsWithChildren, JSX } from 'react';
|
|
2
|
+
import { CreateGtmClientOptions, GtmClient, DataLayerValue, ConsentState, ConsentRegionOptions, ScriptLoadState } from '@jwiedeman/gtm-kit';
|
|
3
|
+
|
|
4
|
+
interface GtmProviderProps extends PropsWithChildren {
|
|
5
|
+
config: CreateGtmClientOptions;
|
|
6
|
+
}
|
|
7
|
+
interface GtmContextValue {
|
|
8
|
+
client: GtmClient;
|
|
9
|
+
push: (value: DataLayerValue) => void;
|
|
10
|
+
setConsentDefaults: (state: ConsentState, options?: ConsentRegionOptions) => void;
|
|
11
|
+
updateConsent: (state: ConsentState, options?: ConsentRegionOptions) => void;
|
|
12
|
+
whenReady: () => Promise<ScriptLoadState[]>;
|
|
13
|
+
onReady: (callback: (state: ScriptLoadState[]) => void) => () => void;
|
|
14
|
+
}
|
|
15
|
+
interface GtmConsentApi {
|
|
16
|
+
setConsentDefaults: (state: ConsentState, options?: ConsentRegionOptions) => void;
|
|
17
|
+
updateConsent: (state: ConsentState, options?: ConsentRegionOptions) => void;
|
|
18
|
+
}
|
|
19
|
+
declare const GtmProvider: ({ config, children }: GtmProviderProps) => JSX.Element;
|
|
20
|
+
declare const useGtm: () => GtmContextValue;
|
|
21
|
+
declare const useGtmClient: () => GtmClient;
|
|
22
|
+
declare const useGtmPush: () => ((value: DataLayerValue) => void);
|
|
23
|
+
declare const useGtmConsent: () => GtmConsentApi;
|
|
24
|
+
declare const useGtmReady: () => (() => Promise<ScriptLoadState[]>);
|
|
25
|
+
|
|
26
|
+
export { GtmConsentApi, GtmContextValue, GtmProvider, GtmProviderProps, useGtm, useGtmClient, useGtmConsent, useGtmPush, useGtmReady };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { createContext, useEffect, useMemo, useRef, useContext } from 'react';
|
|
2
|
+
import { createGtmClient } from '@jwiedeman/gtm-kit';
|
|
3
|
+
import { jsx } from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
var u=createContext(null),d=(t,n)=>{process.env.NODE_ENV!=="production"&&t!==n&&console.warn("[react-gtm-kit] GtmProvider received new configuration; reconfiguration after mount is not supported. The initial configuration will continue to be used.");},G=t=>{let n=useRef(),e=useRef();return n.current?e.current&&d(e.current,t):(n.current=createGtmClient(t),e.current=t),n.current},f=({config:t,children:n})=>{let e=G(t);useEffect(()=>(e.init(),()=>{e.teardown();}),[e]);let p=useMemo(()=>({client:e,push:o=>e.push(o),setConsentDefaults:(o,r)=>e.setConsentDefaults(o,r),updateConsent:(o,r)=>e.updateConsent(o,r),whenReady:()=>e.whenReady(),onReady:o=>e.onReady(o)}),[e]);return jsx(u.Provider,{value:p,children:n})},s=()=>{let t=useContext(u);if(!t)throw new Error("useGtm hook must be used within a GtmProvider instance.");return t},v=s,x=()=>s().client,y=()=>s().push,h=()=>{let{setConsentDefaults:t,updateConsent:n}=s();return useMemo(()=>({setConsentDefaults:t,updateConsent:n}),[t,n])},P=()=>{let{whenReady:t}=s();return t};
|
|
6
|
+
|
|
7
|
+
export { f as GtmProvider, v as useGtm, x as useGtmClient, h as useGtmConsent, y as useGtmPush, P as useGtmReady };
|
|
8
|
+
//# sourceMappingURL=out.js.map
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/provider.tsx"],"names":["createContext","useContext","useEffect","useMemo","useRef","createGtmClient","jsx","GtmContext","warnOnConfigChange","initialConfig","nextConfig","useStableClient","config","clientRef","configRef","GtmProvider","children","client","value","state","options","callback","useGtmContext","context","useGtm","useGtmClient","useGtmPush","useGtmConsent","setConsentDefaults","updateConsent","useGtmReady","whenReady"],"mappings":"AAAA,OAAS,iBAAAA,EAAe,cAAAC,EAAY,aAAAC,EAAW,WAAAC,EAAS,UAAAC,MAAgD,QACxG,OACE,mBAAAC,MAOK,qBAmEE,cAAAC,MAAA,oBA/CT,IAAMC,EAAaP,EAAsC,IAAI,EAEvDQ,EAAqB,CAACC,EAAuCC,IAA6C,CAC1G,QAAQ,IAAI,WAAa,cAAgBD,IAAkBC,GAC7D,QAAQ,KACN,2JAEF,CAEJ,EAEMC,EAAmBC,GAA8C,CACrE,IAAMC,EAAYT,EAAkB,EAC9BU,EAAYV,EAA+B,EAEjD,OAAKS,EAAU,QAGJC,EAAU,SACnBN,EAAmBM,EAAU,QAASF,CAAM,GAH5CC,EAAU,QAAUR,EAAgBO,CAAM,EAC1CE,EAAU,QAAUF,GAKfC,EAAU,OACnB,EAEaE,EAAc,CAAC,CAAE,OAAAH,EAAQ,SAAAI,CAAS,IAAqC,CAClF,IAAMC,EAASN,EAAgBC,CAAM,EAErCV,EAAU,KACRe,EAAO,KAAK,EACL,IAAM,CACXA,EAAO,SAAS,CAClB,GACC,CAACA,CAAM,CAAC,EAEX,IAAMC,EAAQf,EACZ,KAAO,CACL,OAAAc,EACA,KAAOC,GAAUD,EAAO,KAAKC,CAAK,EAClC,mBAAoB,CAACC,EAAOC,IAAYH,EAAO,mBAAmBE,EAAOC,CAAO,EAChF,cAAe,CAACD,EAAOC,IAAYH,EAAO,cAAcE,EAAOC,CAAO,EACtE,UAAW,IAAMH,EAAO,UAAU,EAClC,QAAUI,GAAaJ,EAAO,QAAQI,CAAQ,CAChD,GACA,CAACJ,CAAM,CACT,EAEA,OAAOX,EAACC,EAAW,SAAX,CAAoB,MAAOW,EAAQ,SAAAF,EAAS,CACtD,EAEMM,EAAgB,IAAuB,CAC3C,IAAMC,EAAUtB,EAAWM,CAAU,EACrC,GAAI,CAACgB,EACH,MAAM,IAAI,MAAM,yDAAyD,EAE3E,OAAOA,CACT,EAEaC,EAASF,EAETG,EAAe,IACnBH,EAAc,EAAE,OAGZI,EAAa,IACjBJ,EAAc,EAAE,KAGZK,EAAgB,IAAqB,CAChD,GAAM,CAAE,mBAAAC,EAAoB,cAAAC,CAAc,EAAIP,EAAc,EAC5D,OAAOnB,EAAQ,KAAO,CAAE,mBAAAyB,EAAoB,cAAAC,CAAc,GAAI,CAACD,EAAoBC,CAAa,CAAC,CACnG,EAEaC,EAAc,IAA0C,CACnE,GAAM,CAAE,UAAAC,CAAU,EAAIT,EAAc,EACpC,OAAOS,CACT","sourcesContent":["import { createContext, useContext, useEffect, useMemo, useRef, type PropsWithChildren, type JSX } from 'react';\nimport {\n createGtmClient,\n type ConsentRegionOptions,\n type ConsentState,\n type CreateGtmClientOptions,\n type DataLayerValue,\n type GtmClient,\n type ScriptLoadState\n} from '@jwiedeman/gtm-kit';\n\nexport interface GtmProviderProps extends PropsWithChildren {\n config: CreateGtmClientOptions;\n}\n\nexport interface GtmContextValue {\n client: GtmClient;\n push: (value: DataLayerValue) => void;\n setConsentDefaults: (state: ConsentState, options?: ConsentRegionOptions) => void;\n updateConsent: (state: ConsentState, options?: ConsentRegionOptions) => void;\n whenReady: () => Promise<ScriptLoadState[]>;\n onReady: (callback: (state: ScriptLoadState[]) => void) => () => void;\n}\n\nexport interface GtmConsentApi {\n setConsentDefaults: (state: ConsentState, options?: ConsentRegionOptions) => void;\n updateConsent: (state: ConsentState, options?: ConsentRegionOptions) => void;\n}\n\nconst GtmContext = createContext<GtmContextValue | null>(null);\n\nconst warnOnConfigChange = (initialConfig: CreateGtmClientOptions, nextConfig: CreateGtmClientOptions): void => {\n if (process.env.NODE_ENV !== 'production' && initialConfig !== nextConfig) {\n console.warn(\n '[react-gtm-kit] GtmProvider received new configuration; reconfiguration after mount is not supported. ' +\n 'The initial configuration will continue to be used.'\n );\n }\n};\n\nconst useStableClient = (config: CreateGtmClientOptions): GtmClient => {\n const clientRef = useRef<GtmClient>();\n const configRef = useRef<CreateGtmClientOptions>();\n\n if (!clientRef.current) {\n clientRef.current = createGtmClient(config);\n configRef.current = config;\n } else if (configRef.current) {\n warnOnConfigChange(configRef.current, config);\n }\n\n return clientRef.current!;\n};\n\nexport const GtmProvider = ({ config, children }: GtmProviderProps): JSX.Element => {\n const client = useStableClient(config);\n\n useEffect(() => {\n client.init();\n return () => {\n client.teardown();\n };\n }, [client]);\n\n const value = useMemo<GtmContextValue>(\n () => ({\n client,\n push: (value) => client.push(value),\n setConsentDefaults: (state, options) => client.setConsentDefaults(state, options),\n updateConsent: (state, options) => client.updateConsent(state, options),\n whenReady: () => client.whenReady(),\n onReady: (callback) => client.onReady(callback)\n }),\n [client]\n );\n\n return <GtmContext.Provider value={value}>{children}</GtmContext.Provider>;\n};\n\nconst useGtmContext = (): GtmContextValue => {\n const context = useContext(GtmContext);\n if (!context) {\n throw new Error('useGtm hook must be used within a GtmProvider instance.');\n }\n return context;\n};\n\nexport const useGtm = useGtmContext;\n\nexport const useGtmClient = (): GtmClient => {\n return useGtmContext().client;\n};\n\nexport const useGtmPush = (): ((value: DataLayerValue) => void) => {\n return useGtmContext().push;\n};\n\nexport const useGtmConsent = (): GtmConsentApi => {\n const { setConsentDefaults, updateConsent } = useGtmContext();\n return useMemo(() => ({ setConsentDefaults, updateConsent }), [setConsentDefaults, updateConsent]);\n};\n\nexport const useGtmReady = (): (() => Promise<ScriptLoadState[]>) => {\n const { whenReady } = useGtmContext();\n return whenReady;\n};\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jwiedeman/gtm-kit-react",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "React hooks and provider for GTM Kit - Google Tag Manager integration.",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/jwiedeman/GTM-Kit.git",
|
|
8
|
+
"directory": "packages/react-modern"
|
|
9
|
+
},
|
|
10
|
+
"author": "jwiedeman",
|
|
11
|
+
"keywords": [
|
|
12
|
+
"gtm",
|
|
13
|
+
"google-tag-manager",
|
|
14
|
+
"react",
|
|
15
|
+
"hooks"
|
|
16
|
+
],
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public"
|
|
20
|
+
},
|
|
21
|
+
"type": "module",
|
|
22
|
+
"main": "dist/index.cjs",
|
|
23
|
+
"module": "dist/index.js",
|
|
24
|
+
"types": "dist/index.d.ts",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"import": "./dist/index.js",
|
|
29
|
+
"require": "./dist/index.cjs"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist"
|
|
34
|
+
],
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "tsup",
|
|
37
|
+
"clean": "rm -rf dist",
|
|
38
|
+
"lint": "eslint --max-warnings=0 \"src/**/*.{ts,tsx}\"",
|
|
39
|
+
"test": "jest --config ./jest.config.cjs --runInBand",
|
|
40
|
+
"typecheck": "tsc --noEmit"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@jwiedeman/gtm-kit": "^1.0.1"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@testing-library/jest-dom": "^6.4.2",
|
|
50
|
+
"@testing-library/react": "^14.2.1",
|
|
51
|
+
"@types/react": "^18.3.0",
|
|
52
|
+
"@types/react-dom": "^18.3.0",
|
|
53
|
+
"react": "^18.3.1",
|
|
54
|
+
"react-dom": "^18.3.1",
|
|
55
|
+
"react-router-dom": "^6.27.0",
|
|
56
|
+
"tslib": "^2.6.2"
|
|
57
|
+
}
|
|
58
|
+
}
|