Skip to content

Commit 2bbe018

Browse files
committed
feat: add runtime tracking control with privacy features
1 parent 3fe7e95 commit 2bbe018

File tree

14 files changed

+555
-44
lines changed

14 files changed

+555
-44
lines changed

README.md

Lines changed: 111 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,26 +53,122 @@ That's it! You can now use Nuxt UTM in your Nuxt app ✨
5353

5454
## Usage
5555

56-
You can use `useNuxtUTM` composable to access the UTM object:
56+
### Configuration
57+
58+
You can configure the module by passing options in your `nuxt.config.ts`:
59+
60+
```js
61+
export default defineNuxtConfig({
62+
modules: ['nuxt-utm'],
63+
utm: {
64+
trackingEnabled: true, // defaults to true - initial tracking state
65+
},
66+
})
67+
```
68+
69+
#### Options
70+
71+
- `trackingEnabled`: Boolean (default: `true`) - Sets the initial state for UTM tracking. This can be changed at runtime.
72+
73+
### Runtime Tracking Control
74+
75+
The module provides runtime control over tracking, perfect for implementing cookie consent banners or user privacy preferences.
76+
77+
#### Using the Composable
5778

5879
```vue
5980
<script setup>
6081
const utm = useNuxtUTM()
82+
83+
// The composable returns:
84+
// - data: Reactive array of collected UTM data
85+
// - trackingEnabled: Reactive boolean indicating if tracking is active
86+
// - enableTracking(): Enable UTM tracking
87+
// - disableTracking(): Disable UTM tracking
88+
// - clearData(): Clear all stored UTM data
6189
</script>
6290
```
6391

64-
> Remember: You don't need to import the composable because nuxt imports it automatically.
92+
#### Example: Cookie Banner Integration
93+
94+
```vue
95+
<template>
96+
<div v-if="showBanner" class="cookie-banner">
97+
<p>We use tracking to improve your experience.</p>
98+
<button @click="acceptTracking">Accept</button>
99+
<button @click="rejectTracking">Reject</button>
100+
</div>
101+
</template>
65102
66-
Alternatively, you can get the UTM information through the Nuxt App with the following instructions:
103+
<script setup>
104+
import { ref } from 'vue'
105+
const utm = useNuxtUTM()
106+
const showBanner = ref(!utm.trackingEnabled.value)
107+
108+
const acceptTracking = () => {
109+
utm.enableTracking()
110+
showBanner.value = false
111+
}
112+
113+
const rejectTracking = () => {
114+
utm.disableTracking()
115+
utm.clearData() // Optional: clear any existing data
116+
showBanner.value = false
117+
}
118+
</script>
119+
```
120+
121+
#### Privacy Controls
122+
123+
```vue
124+
<template>
125+
<div class="privacy-settings">
126+
<h3>Privacy Settings</h3>
127+
<label>
128+
<input
129+
type="checkbox"
130+
:checked="utm.trackingEnabled.value"
131+
@change="toggleTracking"
132+
/>
133+
Enable UTM tracking
134+
</label>
135+
<button @click="utm.clearData" v-if="utm.data.value.length > 0">
136+
Clear tracking data ({{ utm.data.value.length }} entries)
137+
</button>
138+
</div>
139+
</template>
140+
141+
<script setup>
142+
const utm = useNuxtUTM()
143+
144+
const toggleTracking = (event) => {
145+
if (event.target.checked) {
146+
utm.enableTracking()
147+
} else {
148+
utm.disableTracking()
149+
}
150+
}
151+
</script>
152+
```
153+
154+
### Accessing UTM Data
155+
156+
You can use `useNuxtUTM` composable to access the UTM data:
67157

68158
```vue
69159
<script setup>
70-
import { useNuxtApp } from 'nuxt/app'
71-
const { $utm } = useNuxtApp()
160+
const utm = useNuxtUTM()
161+
162+
// Access the collected data
163+
console.log(utm.data.value)
72164
</script>
73165
```
74166

75-
Regardless of the option you choose to use the module, the `utm' object will contain an array of UTM parameters collected for use. Each element in the array represents a set of UTM parameters collected from a URL visit, and is structured as follows
167+
> Remember: You don't need to import the composable because Nuxt imports it automatically.
168+
169+
### Data Structure
170+
171+
The `data` property contains an array of UTM parameters collected. Each element in the array represents a set of UTM parameters collected from a URL visit, and is structured as follows
76172

77173
```json
78174
[
@@ -104,7 +200,15 @@ Regardless of the option you choose to use the module, the `utm' object will con
104200
]
105201
```
106202

107-
In the `$utm` array, each entry provides a `timestamp` indicating when the UTM parameters were collected, the `utmParams` object containing the UTM parameters, `additionalInfo` object with more context about the visit, and a `sessionId` to differentiate visits in different sessions.
203+
Each entry provides a `timestamp` indicating when the UTM parameters were collected, the `utmParams` object containing the UTM parameters, `additionalInfo` object with more context about the visit, and a `sessionId` to differentiate visits in different sessions.
204+
205+
### Key Features
206+
207+
- **Runtime Control**: Enable/disable tracking dynamically based on user consent
208+
- **Privacy Friendly**: Respects user preferences and provides clear data management
209+
- **Persistent Preferences**: Tracking preferences are saved and persist across sessions
210+
- **Data Clearing**: Ability to completely remove all collected data
211+
- **Session Management**: Automatically manages sessions to avoid duplicate tracking
108212

109213
## Development
110214

playground/app.vue

Lines changed: 138 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,146 @@
11
<template>
2-
<div>Nuxt 3 UTM module playground!</div>
3-
<pre>{{ utm }}</pre>
2+
<div class="container">
3+
<h1>Nuxt UTM Module Playground</h1>
4+
5+
<div class="controls">
6+
<h2>Tracking Controls</h2>
7+
<p>
8+
Tracking is currently:
9+
<strong :class="{ enabled: utm.trackingEnabled.value, disabled: !utm.trackingEnabled.value }">
10+
{{ utm.trackingEnabled.value ? 'ENABLED' : 'DISABLED' }}
11+
</strong>
12+
</p>
13+
14+
<div class="buttons">
15+
<button
16+
:disabled="utm.trackingEnabled.value"
17+
@click="utm.enableTracking"
18+
>
19+
Enable Tracking
20+
</button>
21+
<button
22+
:disabled="!utm.trackingEnabled.value"
23+
@click="utm.disableTracking"
24+
>
25+
Disable Tracking
26+
</button>
27+
<button
28+
class="danger"
29+
@click="utm.clearData"
30+
>
31+
Clear All Data
32+
</button>
33+
</div>
34+
35+
<div class="info">
36+
<p>Try visiting with UTM parameters:</p>
37+
<a href="/?utm_source=test&utm_medium=demo&utm_campaign=playground">
38+
Add UTM params to URL
39+
</a>
40+
</div>
41+
</div>
42+
43+
<div class="data">
44+
<h2>Collected UTM Data ({{ utm.data.value.length }} entries)</h2>
45+
<pre>{{ utm.data.value }}</pre>
46+
</div>
47+
</div>
448
</template>
549

650
<script setup>
751
import { useNuxtUTM } from '#imports'
852
953
const utm = useNuxtUTM()
1054
</script>
55+
56+
<style scoped>
57+
.container {
58+
max-width: 1200px;
59+
margin: 0 auto;
60+
padding: 2rem;
61+
font-family: system-ui, -apple-system, sans-serif;
62+
}
63+
64+
h1 {
65+
color: #00dc82;
66+
margin-bottom: 2rem;
67+
}
68+
69+
.controls {
70+
background: #f5f5f5;
71+
padding: 1.5rem;
72+
border-radius: 8px;
73+
margin-bottom: 2rem;
74+
}
75+
76+
.buttons {
77+
display: flex;
78+
gap: 1rem;
79+
margin: 1rem 0;
80+
}
81+
82+
button {
83+
padding: 0.5rem 1rem;
84+
border: none;
85+
border-radius: 4px;
86+
background: #00dc82;
87+
color: white;
88+
cursor: pointer;
89+
font-size: 1rem;
90+
}
91+
92+
button:hover:not(:disabled) {
93+
background: #00c76d;
94+
}
95+
96+
button:disabled {
97+
opacity: 0.5;
98+
cursor: not-allowed;
99+
}
100+
101+
button.danger {
102+
background: #dc3545;
103+
}
104+
105+
button.danger:hover {
106+
background: #c82333;
107+
}
108+
109+
.enabled {
110+
color: #28a745;
111+
}
112+
113+
.disabled {
114+
color: #dc3545;
115+
}
116+
117+
.info {
118+
margin-top: 1rem;
119+
padding-top: 1rem;
120+
border-top: 1px solid #ddd;
121+
}
122+
123+
.info a {
124+
color: #00dc82;
125+
text-decoration: none;
126+
}
127+
128+
.info a:hover {
129+
text-decoration: underline;
130+
}
131+
132+
.data {
133+
background: #f8f9fa;
134+
padding: 1.5rem;
135+
border-radius: 8px;
136+
}
137+
138+
pre {
139+
background: white;
140+
padding: 1rem;
141+
border-radius: 4px;
142+
overflow-x: auto;
143+
max-height: 400px;
144+
overflow-y: auto;
145+
}
146+
</style>

playground/nuxt.config.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
export default defineNuxtConfig({
22
modules: ['../src/module'],
33
devtools: { enabled: true },
4-
utm: {},
4+
app: {
5+
head: {
6+
title: 'Nuxt UTM Playground',
7+
},
8+
},
9+
utm: {
10+
trackingEnabled: true,
11+
},
512
})

src/module.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import { defineNuxtModule, addPlugin, addImports, createResolver } from '@nuxt/kit'
22

3-
// Module options TypeScript interface definition
4-
/* eslint-disable @typescript-eslint/no-empty-object-type */
5-
export interface ModuleOptions {}
6-
/* eslint-enable @typescript-eslint/no-empty-object-type */
3+
export interface ModuleOptions {
4+
trackingEnabled?: boolean
5+
}
76

87
export default defineNuxtModule<ModuleOptions>({
98
meta: {
@@ -13,12 +12,16 @@ export default defineNuxtModule<ModuleOptions>({
1312
nuxt: '^3.0.0 || ^4.0.0',
1413
},
1514
},
16-
// Default configuration options of the Nuxt module
17-
defaults: {},
18-
setup() {
15+
defaults: {
16+
trackingEnabled: true,
17+
},
18+
setup(options, nuxt) {
1919
const resolver = createResolver(import.meta.url)
2020

21-
// Do not add the extension since the `.ts` will be transpiled to `.mjs` after `npm run prepack`
21+
nuxt.options.runtimeConfig.public.utm = {
22+
trackingEnabled: options.trackingEnabled ?? true,
23+
}
24+
2225
addPlugin(resolver.resolve('./runtime/plugin'))
2326
addImports({
2427
name: 'useNuxtUTM',

src/runtime/composables.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,23 @@
1+
import type { Ref } from 'vue'
2+
import type { DataObject } from 'nuxt-utm'
13
import { useNuxtApp } from '#imports'
24

3-
export const useNuxtUTM = () => {
5+
export interface UseNuxtUTMReturn {
6+
data: Readonly<Ref<readonly DataObject[]>>
7+
trackingEnabled: Readonly<Ref<boolean>>
8+
enableTracking: () => void
9+
disableTracking: () => void
10+
clearData: () => void
11+
}
12+
13+
export const useNuxtUTM = (): UseNuxtUTMReturn => {
414
const nuxtApp = useNuxtApp()
5-
return nuxtApp.$utm
15+
16+
return {
17+
data: nuxtApp.$utm,
18+
trackingEnabled: nuxtApp.$utmTrackingEnabled,
19+
enableTracking: nuxtApp.$utmEnableTracking,
20+
disableTracking: nuxtApp.$utmDisableTracking,
21+
clearData: nuxtApp.$utmClearData,
22+
}
623
}

0 commit comments

Comments
 (0)