├── .babelrc
├── .eslintignore
├── .eslintrc
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── package-lock.json
├── package.json
└── src
├── Snippets.js
├── TagManager.js
├── __tests__
├── Snippets.spec.js
├── TagManager.spec.js
└── warn.spec.js
├── index.js
└── utils
└── warn.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "react",
4 | "es2015"
5 | ]
6 | }
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | __tests__
3 | coverage
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "extends": "eslint:recommended",
4 | "env": {
5 | "es6": true,
6 | "node": true,
7 | "browser": true,
8 | "jest": true
9 | },
10 | "parserOptions": {
11 | "ecmaFeatures": {
12 | "jsx": true
13 | }
14 | },
15 | "rules": {
16 | "consistent-this": 0,
17 | "no-console": 0
18 | }
19 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | *.log
3 | coverage
4 | .DS_Store
5 | dist
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '6.11.0'
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 HolidayCheck
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://travis-ci.org/alinemorelli/react-gtm)
2 | [](https://www.npmjs.com/package/react-gtm-module)
3 | [](https://www.npmjs.com/package/react-gtm-module)
4 |
5 | # react-gtm-module
6 | ### React Google Tag Manager Module
7 |
8 | This is a Javascript module to [React](https://facebook.github.io/react/) based apps that implement Google Tag Manager. It is designed to use [GTM](https://developers.google.com/tag-manager/quickstart) snippet.
9 |
10 | You can easily use custom dataLayer, multiple dataLayers and additional events.
11 |
12 | ## Installation
13 |
14 | [npm](https://www.npmjs.com/):
15 |
16 | ```bash
17 | npm install react-gtm-module --save
18 | ```
19 |
20 | ## Usage
21 |
22 | Initializing GTM Module:
23 |
24 | ```js
25 | import React from 'react'
26 | import ReactDOM from 'react-dom'
27 | import Router from 'react-router'
28 | import routes from './routes'
29 |
30 | ...
31 | import TagManager from 'react-gtm-module'
32 |
33 | const tagManagerArgs = {
34 | gtmId: 'GTM-000000'
35 | }
36 |
37 | TagManager.initialize(tagManagerArgs)
38 | ...
39 |
40 | const app = document.getElementById('app')
41 | ReactDOM.render(, app)
42 |
43 | ```
44 |
45 | ## DataLayer
46 |
47 | ### Custom dataLayer example:
48 |
49 | ```js
50 | import React from 'react'
51 | import ReactDOM from 'react-dom'
52 | import Router from 'react-router'
53 | import routes from './routes'
54 |
55 | ...
56 | import TagManager from 'react-gtm-module'
57 |
58 | const tagManagerArgs = {
59 | gtmId: 'GTM-000000',
60 | dataLayer: {
61 | userId: '001',
62 | userProject: 'project'
63 | }
64 | }
65 |
66 | TagManager.initialize(tagManagerArgs)
67 | ...
68 |
69 | const app = document.getElementById('app')
70 | ReactDOM.render(, app)
71 |
72 | ```
73 |
74 |
75 | ### Multiple dataLayer example:
76 |
77 | If you need send multiple custom dataLayer you can initialize GTM Module on different components sending different dataLayers
78 |
79 | You can initialize it normally:
80 |
81 | ```js
82 | import React from 'react'
83 | import ReactDOM from 'react-dom'
84 | import Router from 'react-router'
85 | import routes from './routes'
86 |
87 | ...
88 | import TagManager from 'react-gtm-module'
89 |
90 | const tagManagerArgs = {
91 | gtmId: 'GTM-000000',
92 | dataLayerName: 'PageDataLayer'
93 | }
94 |
95 | TagManager.initialize(tagManagerArgs)
96 | ...
97 |
98 | const app = document.getElementById('app')
99 | ReactDOM.render(, app)
100 |
101 | ```
102 |
103 | And send your data in each page you want
104 |
105 | ```js
106 | import React from 'react'
107 |
108 | ...
109 | import TagManager from 'react-gtm-module'
110 |
111 | const tagManagerArgs = {
112 | dataLayer: {
113 | userId: '001',
114 | userProject: 'project',
115 | page: 'home'
116 | },
117 | dataLayerName: 'PageDataLayer'
118 | }
119 | ...
120 |
121 | const Home = () => {
122 | ...
123 | TagManager.dataLayer(tagManagerArgs)
124 | ...
125 |
126 | return (
127 |
128 | //your component code
129 |
130 | )
131 | }
132 |
133 | export default Home
134 |
135 | ```
136 |
137 |
138 | ## Events
139 |
140 | ### Example:
141 |
142 | ```js
143 | import React from 'react'
144 | import ReactDOM from 'react-dom'
145 | import Router from 'react-router'
146 | import routes from './routes'
147 |
148 | ...
149 | import TagManager from 'react-gtm-module'
150 |
151 | const tagManagerArgs = {
152 | gtmId: 'GTM-000000',
153 | events: {
154 | sendUserInfo: 'userInfo'
155 | }
156 | }
157 |
158 | TagManager.initialize(tagManagerArgs)
159 | ...
160 |
161 | const app = document.getElementById('app')
162 | ReactDOM.render(, app)
163 | ```
164 |
165 | ## Environments
166 |
167 | Configure how Tag Manager will works between development and production server environments.
168 |
169 | ### Example:
170 |
171 | ```js
172 | import React from 'react'
173 | import ReactDOM from 'react-dom'
174 | import Router from 'react-router'
175 | import routes from './routes'
176 |
177 | ...
178 | import TagManager from 'react-gtm-module'
179 |
180 | const tagManagerArgs = {
181 | gtmId: 'GTM-000000',
182 | auth: '6sBOnZx1hqPcO01xPOytLK',
183 | preview: 'env-2'
184 | }
185 |
186 | TagManager.initialize(tagManagerArgs)
187 |
188 | ```
189 |
190 | ##### How can I find auth and preview?
191 | Go to Google Tag Manager -> ADMIN -> Environments -> Actions -> Get Snippet.
192 | Look for gtm_auth and gtm_preview
193 |
194 | ##### Don't know to use GTM environments?
195 | - https://support.google.com/tagmanager/answer/6311518
196 | - https://www.simoahava.com/analytics/better-qa-with-google-tag-manager-environments/
197 |
198 |
199 |
200 | |Value|Type|Required|Notes|
201 | |------|-----|-----|-----|
202 | |gtmId| `String`| Yes | GTM id, must be something like `GTM-000000`.|
203 | |dataLayer| `Object`| No | Object that contains all of the information that you want to pass to Google Tag Manager.|
204 | |dataLayerName| `String`| No | Custom name for dataLayer object.|
205 | |events| `Object`| No | Additional events such as 'gtm.start': new Date().getTime(),event:'gtm.js'.|
206 | |auth| `String` | No | used to set environments. |
207 | |preview| `String` | No | used to set environments, something like `env-00`. |
208 |
209 |
210 | ### Note:
211 |
212 | - Disabling javascript in the browser can prevent the correct operation of this library if React is only being rendered on the client side.
213 |
214 | - Before implementing GTM in your application ensure that you have at least one published container, otherwise Google Tag Manager snippet will return 404.
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-gtm-module",
3 | "version": "2.0.11",
4 | "description": "React Google Tag Manager Module",
5 | "main": "dist/index.js",
6 | "scripts": {
7 | "lint": "eslint .",
8 | "jest": "jest",
9 | "test": "npm run lint && npm run jest",
10 | "build": "babel src -d dist --ignore src/__tests__",
11 | "prepublish": "npm run build"
12 | },
13 | "repository": {
14 | "type": "git",
15 | "url": "https://github.com/alinemorelli/react-gtm"
16 | },
17 | "author": {
18 | "name": "Aline Morelli",
19 | "email": "aline.morelliw@gmail.com",
20 | "url": "https://github.com/alinemorelli"
21 | },
22 | "license": "MIT",
23 | "bugs": {
24 | "url": "https://github.com/alinemorelli/react-gtm/issues"
25 | },
26 | "homepage": "",
27 | "keywords": [
28 | "react",
29 | "reactjs",
30 | "react-component",
31 | "google tag manager",
32 | "tag manager",
33 | "gtm"
34 | ],
35 | "devDependencies": {
36 | "babel-cli": "^6.24.1",
37 | "babel-eslint": "^7.2.3",
38 | "babel-preset-es2015": "^6.24.1",
39 | "babel-preset-react": "^6.24.1",
40 | "babel-register": "6.18.0",
41 | "enzyme": "^3.3.0",
42 | "eslint": "^4.18.2",
43 | "jest": "^19.0.2",
44 | "jest-cli": "^21.2.1",
45 | "sinon": "^1.14.1"
46 | },
47 | "jest": {
48 | "testRegex": "__tests__.*\\.spec.js$",
49 | "collectCoverage": true
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/Snippets.js:
--------------------------------------------------------------------------------
1 | import warn from './utils/warn'
2 |
3 | // https://developers.google.com/tag-manager/quickstart
4 |
5 | const Snippets = {
6 | tags: function ({ id, events, dataLayer, dataLayerName, preview, auth }) {
7 | const gtm_auth = `>m_auth=${auth}`
8 | const gtm_preview = `>m_preview=${preview}`
9 |
10 | if (!id) warn('GTM Id is required')
11 |
12 | const iframe = `
13 | `
15 |
16 | const script = `
17 | (function(w,d,s,l,i){w[l]=w[l]||[];
18 | w[l].push({'gtm.start': new Date().getTime(),event:'gtm.js', ${JSON.stringify(events).slice(1, -1)}});
19 | var f=d.getElementsByTagName(s)[0],j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';
20 | j.async=true;j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl+'${gtm_auth}${gtm_preview}>m_cookies_win=x';
21 | f.parentNode.insertBefore(j,f);
22 | })(window,document,'script','${dataLayerName}','${id}');`
23 |
24 | const dataLayerVar = this.dataLayer(dataLayer, dataLayerName)
25 |
26 | return {
27 | iframe,
28 | script,
29 | dataLayerVar
30 | }
31 | },
32 | dataLayer: function (dataLayer, dataLayerName) {
33 | return `
34 | window.${dataLayerName} = window.${dataLayerName} || [];
35 | window.${dataLayerName}.push(${JSON.stringify(dataLayer)})`
36 | }
37 | }
38 |
39 | module.exports = Snippets
40 |
--------------------------------------------------------------------------------
/src/TagManager.js:
--------------------------------------------------------------------------------
1 | import Snippets from './Snippets'
2 |
3 | const TagManager = {
4 | dataScript: function (dataLayer) {
5 | const script = document.createElement('script')
6 | script.innerHTML = dataLayer
7 | return script
8 | },
9 | gtm: function (args) {
10 | const snippets = Snippets.tags(args)
11 |
12 | const noScript = () => {
13 | const noscript = document.createElement('noscript')
14 | noscript.innerHTML = snippets.iframe
15 | return noscript
16 | }
17 |
18 | const script = () => {
19 | const script = document.createElement('script')
20 | script.innerHTML = snippets.script
21 | return script
22 | }
23 |
24 | const dataScript = this.dataScript(snippets.dataLayerVar)
25 |
26 | return {
27 | noScript,
28 | script,
29 | dataScript
30 | }
31 | },
32 | initialize: function ({ gtmId, events = {}, dataLayer, dataLayerName = 'dataLayer', auth = '', preview = '' }) {
33 | const gtm = this.gtm({
34 | id: gtmId,
35 | events: events,
36 | dataLayer: dataLayer || undefined,
37 | dataLayerName: dataLayerName,
38 | auth,
39 | preview
40 | })
41 | if (dataLayer) document.head.appendChild(gtm.dataScript)
42 | document.head.insertBefore(gtm.script(), document.head.childNodes[0])
43 | document.body.insertBefore(gtm.noScript(), document.body.childNodes[0])
44 | },
45 | dataLayer: function ({dataLayer, dataLayerName = 'dataLayer'}) {
46 | if (window[dataLayerName]) return window[dataLayerName].push(dataLayer)
47 | const snippets = Snippets.dataLayer(dataLayer, dataLayerName)
48 | const dataScript = this.dataScript(snippets)
49 | document.head.insertBefore(dataScript, document.head.childNodes[0])
50 | }
51 | }
52 |
53 | module.exports = TagManager
54 |
--------------------------------------------------------------------------------
/src/__tests__/Snippets.spec.js:
--------------------------------------------------------------------------------
1 | import Snippets from '../Snippets'
2 |
3 | let args
4 | let snippets
5 |
6 | describe('Snippets', () => {
7 | beforeEach(() => {
8 | args = { id: 'GTM-abc123', dataLayerName: 'dataLayer', events: {} }
9 | snippets = Snippets.tags(args)
10 | })
11 |
12 | it('should use the `id` for the iframe', () => {
13 | expect(snippets.iframe).toContain(`id=${args.id}`, 1)
14 | })
15 |
16 | it('should use the `gtm_auth` and `gtm_preview` for the iframe', () => {
17 | Object.assign(args, {
18 | auth: '6sBOnZx1hqPcO01xPOytLK',
19 | preview: 'env-2'
20 | })
21 | snippets = Snippets.tags(args)
22 | expect(snippets.iframe).toContain(`gtm_auth=${args.auth}`, 1)
23 | expect(snippets.iframe).toContain(`gtm_preview=${args.preview}`, 1)
24 | })
25 |
26 | it('should use the `dataLayer` for the script', () => {
27 | args = { dataLayer: { name: 'test' } }
28 | const dataLayerName = 'dataLayer'
29 | snippets = Snippets.dataLayer(args, dataLayerName)
30 | console.log(snippets)
31 | expect(snippets).toContain('{"name":"test"}')
32 | })
33 |
34 | it('no id provided should log a warn', () => {
35 | console.warn = jest.fn()
36 | const noIdArgs = { dataLayerName: 'dataLayer', events: {} }
37 | Snippets.tags(noIdArgs)
38 | expect(console.warn).toBeCalled()
39 | })
40 | })
--------------------------------------------------------------------------------
/src/__tests__/TagManager.spec.js:
--------------------------------------------------------------------------------
1 | import TagManager from '../TagManager'
2 |
3 | describe('TagManager', () => {
4 | it('should render tagmanager', () => {
5 | TagManager.initialize({gtmId: 'GTM-000000'})
6 | expect(window.dataLayer).toHaveLength(1)
7 | })
8 |
9 | it('should render datalayer', () => {
10 | const gtmArgs = {
11 | gtmId: 'GTM-000000',
12 | dataLayer: {
13 | userInfo: 'userInfo'
14 | }
15 | }
16 | TagManager.initialize(gtmArgs)
17 | expect(window.dataLayer).toHaveLength(1)
18 | })
19 | })
--------------------------------------------------------------------------------
/src/__tests__/warn.spec.js:
--------------------------------------------------------------------------------
1 | import sinon from 'sinon'
2 | import warn from '../utils/warn'
3 |
4 | describe('warn()', function () {
5 | it('should append [react-gtm] to warning messages', () => {
6 | sinon.stub(console, 'warn')
7 | warn('foo')
8 | console.warn.toHaveBeenCalled
9 | })
10 | })
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import TagManager from './TagManager'
2 |
3 | module.exports = TagManager
--------------------------------------------------------------------------------
/src/utils/warn.js:
--------------------------------------------------------------------------------
1 | const warn = (s) => {
2 | console.warn('[react-gtm]', s);
3 | }
4 |
5 | export default warn
--------------------------------------------------------------------------------