Skip to content

Commit 28ce259

Browse files
feat: Reddit Pixel (#507)
Co-authored-by: Dennis <[email protected]>
1 parent 3c01eb8 commit 28ce259

File tree

7 files changed

+269
-0
lines changed

7 files changed

+269
-0
lines changed
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
---
2+
title: Reddit Pixel
3+
description: Use Reddit Pixel in your Nuxt app.
4+
links:
5+
- label: Source
6+
icon: i-simple-icons-github
7+
to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/reddit-pixel.ts
8+
size: xs
9+
---
10+
11+
[Reddit Pixel](https://advertising.reddithelp.com/en/categories/custom-audiences-and-conversion-tracking/reddit-pixel) helps you track conversions and build audiences for your Reddit advertising campaigns.
12+
13+
Nuxt Scripts provides a registry script composable `useScriptRedditPixel` to easily integrate Reddit Pixel in your Nuxt app.
14+
15+
### Nuxt Config Setup
16+
17+
The simplest way to load Reddit Pixel globally in your Nuxt App is to use Nuxt config. Alternatively you can directly
18+
use the [useScriptRedditPixel](#useScriptRedditPixel) composable.
19+
20+
If you don't plan to send custom events you can use the [Environment overrides](https://nuxt.com/docs/getting-started/configuration#environment-overrides) to
21+
disable the script in development.
22+
23+
::code-group
24+
25+
```ts [Always enabled]
26+
export default defineNuxtConfig({
27+
scripts: {
28+
registry: {
29+
redditPixel: {
30+
id: 'YOUR_ID'
31+
}
32+
}
33+
}
34+
})
35+
```
36+
37+
```ts [Production only]
38+
export default defineNuxtConfig({
39+
$production: {
40+
scripts: {
41+
registry: {
42+
redditPixel: {
43+
id: 'YOUR_ID',
44+
}
45+
}
46+
}
47+
}
48+
})
49+
```
50+
51+
::
52+
53+
#### With Environment Variables
54+
55+
If you prefer to configure your id using environment variables.
56+
57+
```ts [nuxt.config.ts]
58+
export default defineNuxtConfig({
59+
scripts: {
60+
registry: {
61+
redditPixel: true,
62+
}
63+
},
64+
// you need to provide a runtime config to access the environment variables
65+
runtimeConfig: {
66+
public: {
67+
scripts: {
68+
redditPixel: {
69+
id: '', // NUXT_PUBLIC_SCRIPTS_REDDIT_PIXEL_ID
70+
},
71+
},
72+
},
73+
},
74+
})
75+
```
76+
77+
```text [.env]
78+
NUXT_PUBLIC_SCRIPTS_REDDIT_PIXEL_ID=<YOUR_ID>
79+
```
80+
81+
## useScriptRedditPixel
82+
83+
The `useScriptRedditPixel` composable lets you have fine-grain control over when and how Reddit Pixel is loaded on your site.
84+
85+
```ts
86+
const { proxy } = useScriptRedditPixel({
87+
id: 'YOUR_ID'
88+
})
89+
// example
90+
proxy.rdt('track', 'Lead')
91+
```
92+
93+
Please follow the [Registry Scripts](/docs/guides/registry-scripts) guide to learn more about advanced usage.
94+
95+
### RedditPixelApi
96+
97+
```ts
98+
export interface RedditPixelApi {
99+
rdt: RdtFns & {
100+
sendEvent: (rdt: RedditPixelApi['rdt'], args: unknown[]) => void
101+
callQueue: unknown[]
102+
}
103+
}
104+
type RdtFns
105+
= & ((event: 'init', id: string) => void)
106+
& ((event: 'track', eventName: string) => void)
107+
```
108+
109+
### Config Schema
110+
111+
You must provide the options when setting up the script for the first time.
112+
113+
```ts
114+
export const RedditPixelOptions = object({
115+
id: string(),
116+
})
117+
```
118+
119+
## Example
120+
121+
Using Reddit Pixel only in production while using `rdt` to send a tracking event.
122+
123+
::code-group
124+
125+
```vue [TrackingButton.vue]
126+
<script setup lang="ts">
127+
const { proxy } = useScriptRedditPixel()
128+
129+
// noop in development, ssr
130+
// just works in production, client
131+
function trackConversion() {
132+
proxy.rdt('track', 'Lead')
133+
}
134+
</script>
135+
136+
<template>
137+
<div>
138+
<button @click="trackConversion">
139+
Track Conversion
140+
</button>
141+
</div>
142+
</template>
143+
```
144+
145+
::

playground/pages/index.vue

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ const thirdParties = [
2626
name: 'X Pixel',
2727
path: '/third-parties/x-pixel/nuxt-scripts',
2828
},
29+
{
30+
name: 'Reddit Pixel',
31+
path: '/third-parties/reddit-pixel/nuxt-scripts',
32+
},
2933
{
3034
name: 'Google Adsense',
3135
path: '/third-parties/google-adsense/nuxt-scripts',
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<script lang="ts" setup>
2+
import { useHead } from '#imports'
3+
4+
useHead({
5+
script: [
6+
{ innerHTML: `!(function (w, d) { if (!w.rdt) { var p = w.rdt = function () { p.sendEvent ? p.sendEvent.apply(p, arguments) : p.callQueue.push(arguments) } p.callQueue = [] var t = d.createElement('script') t.src = 'https://www.redditstatic.com/ads/pixel.js', t.async = !0 var s = d.getElementsByTagName('script')[0] s.parentNode.insertBefore(t, s) } }(window, document)) rdt('init', 'YOUR_PIXEL_ID') rdt('track', 'PageVisit')` },
7+
],
8+
})
9+
10+
function triggerEvent() {
11+
window.rdt('init', 'YOUR_PIXEL_ID')
12+
window.rdt('track', 'PageVisit')
13+
}
14+
</script>
15+
16+
<template>
17+
<div>
18+
<UButton @click="() => triggerEvent()">
19+
Trigger Event
20+
</UButton>
21+
</div>
22+
</template>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<script lang="ts" setup>
2+
import { useHead, useScriptRedditPixel } from '#imports'
3+
4+
useHead({
5+
title: 'Reddit',
6+
})
7+
8+
// composables return the underlying api as a proxy object and the script state
9+
const { status, rdt } = useScriptRedditPixel({ id: 'YOUR_PIXEL_ID' })
10+
// this will be triggered once the script is ready async
11+
function triggerEvent() {
12+
rdt('track', 'PageVisit')
13+
}
14+
</script>
15+
16+
<template>
17+
<div>
18+
<ClientOnly>
19+
<div>
20+
status: {{ status }}
21+
</div>
22+
<UButton @click="triggerEvent">
23+
Trigger Event
24+
</UButton>
25+
</ClientOnly>
26+
</div>
27+
</template>

src/registry.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,16 @@ export async function registry(resolve?: (path: string, opts?: ResolvePathOption
121121
from: await resolve('./runtime/registry/snapchat-pixel'),
122122
},
123123
},
124+
{
125+
label: 'Reddit Pixel',
126+
src: 'https://www.redditstatic.com/ads/pixel.js',
127+
category: 'tracking',
128+
logo: `<svg viewBox="0 0 800 800" xmlns="http://www.w3.org/2000/svg" width="32" height="32"> <circle cx="400" cy="400" fill="#ff4500" r="400"/> <path d="M666.8 400c.08 5.48-.6 10.95-2.04 16.24s-3.62 10.36-6.48 15.04c-2.85 4.68-6.35 8.94-10.39 12.65s-8.58 6.83-13.49 9.27c.11 1.46.2 2.93.25 4.4a107.268 107.268 0 0 1 0 8.8c-.05 1.47-.14 2.94-.25 4.4 0 89.6-104.4 162.4-233.2 162.4S168 560.4 168 470.8c-.11-1.46-.2-2.93-.25-4.4a107.268 107.268 0 0 1 0-8.8c.05-1.47.14-2.94.25-4.4a58.438 58.438 0 0 1-31.85-37.28 58.41 58.41 0 0 1 7.8-48.42 58.354 58.354 0 0 1 41.93-25.4 58.4 58.4 0 0 1 46.52 15.5 286.795 286.795 0 0 1 35.89-20.71c12.45-6.02 25.32-11.14 38.51-15.3s26.67-7.35 40.32-9.56 27.45-3.42 41.28-3.63L418 169.6c.33-1.61.98-3.13 1.91-4.49.92-1.35 2.11-2.51 3.48-3.4 1.38-.89 2.92-1.5 4.54-1.8 1.61-.29 3.27-.26 4.87.09l98 19.6c9.89-16.99 30.65-24.27 48.98-17.19s28.81 26.43 24.71 45.65c-4.09 19.22-21.55 32.62-41.17 31.61-19.63-1.01-35.62-16.13-37.72-35.67L440 186l-26 124.8c13.66.29 27.29 1.57 40.77 3.82a284.358 284.358 0 0 1 77.8 24.86A284.412 284.412 0 0 1 568 360a58.345 58.345 0 0 1 29.4-15.21 58.361 58.361 0 0 1 32.95 3.21 58.384 58.384 0 0 1 25.91 20.61A58.384 58.384 0 0 1 666.8 400zm-396.96 55.31c2.02 4.85 4.96 9.26 8.68 12.97 3.71 3.72 8.12 6.66 12.97 8.68A40.049 40.049 0 0 0 306.8 480c16.18 0 30.76-9.75 36.96-24.69 6.19-14.95 2.76-32.15-8.68-43.59s-28.64-14.87-43.59-8.68c-14.94 6.2-24.69 20.78-24.69 36.96 0 5.25 1.03 10.45 3.04 15.31zm229.1 96.02c2.05-2 3.22-4.73 3.26-7.59.04-2.87-1.07-5.63-3.07-7.68s-4.73-3.22-7.59-3.26c-2.87-.04-5.63 1.07-7.94 2.8a131.06 131.06 0 0 1-19.04 11.35 131.53 131.53 0 0 1-20.68 7.99c-7.1 2.07-14.37 3.54-21.72 4.39-7.36.85-14.77 1.07-22.16.67-7.38.33-14.78.03-22.11-.89a129.01 129.01 0 0 1-21.64-4.6c-7.08-2.14-13.95-4.88-20.56-8.18s-12.93-7.16-18.89-11.53c-2.07-1.7-4.7-2.57-7.38-2.44s-5.21 1.26-7.11 3.15c-1.89 1.9-3.02 4.43-3.15 7.11s.74 5.31 2.44 7.38c7.03 5.3 14.5 9.98 22.33 14s16 7.35 24.4 9.97 17.01 4.51 25.74 5.66c8.73 1.14 17.54 1.53 26.33 1.17 8.79.36 17.6-.03 26.33-1.17A153.961 153.961 0 0 0 476.87 564c7.83-4.02 15.3-8.7 22.33-14zm-7.34-68.13c5.42.06 10.8-.99 15.81-3.07 5.01-2.09 9.54-5.17 13.32-9.06s6.72-8.51 8.66-13.58A39.882 39.882 0 0 0 532 441.6c0-16.18-9.75-30.76-24.69-36.96-14.95-6.19-32.15-2.76-43.59 8.68s-14.87 28.64-8.68 43.59c6.2 14.94 20.78 24.69 36.96 24.69z" fill="#fff"/> </svg>`,
129+
import: {
130+
name: 'useScriptRedditPixel',
131+
from: await resolve('./runtime/registry/reddit-pixel'),
132+
},
133+
},
124134
// ads
125135
{
126136
label: 'Google Adsense',
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import type { UseScriptInput } from '@unhead/vue'
2+
import { useRegistryScript } from '../utils'
3+
import { object, string } from '#nuxt-scripts-validator'
4+
import type { RegistryScriptInput } from '#nuxt-scripts/types'
5+
6+
type RdtFns
7+
= & ((event: 'init', id: string) => void)
8+
& ((event: 'track', eventName: string) => void)
9+
10+
export interface RedditPixelApi {
11+
rdt: RdtFns & {
12+
sendEvent: (rdt: RedditPixelApi['rdt'], args: unknown[]) => void
13+
callQueue: unknown[]
14+
}
15+
}
16+
17+
declare global {
18+
interface Window extends RedditPixelApi {}
19+
}
20+
21+
export const RedditPixelOptions = object({
22+
id: string(),
23+
})
24+
export type RedditPixelInput = RegistryScriptInput<typeof RedditPixelOptions, true, false, false>
25+
26+
export function useScriptRedditPixel<T extends RedditPixelApi>(_options?: RedditPixelInput) {
27+
return useRegistryScript<T, typeof RedditPixelOptions>('redditPixel', (options) => {
28+
return ({
29+
scriptInput: {
30+
src: 'https://www.redditstatic.com/ads/pixel.js',
31+
async: true,
32+
} as UseScriptInput,
33+
clientInit: import.meta.server
34+
? undefined
35+
: () => {
36+
const rdt = function (...args: unknown[]) {
37+
if ((rdt as any).sendEvent) {
38+
(rdt as any).sendEvent(rdt, args)
39+
}
40+
else {
41+
(rdt as any).callQueue.push(args)
42+
}
43+
} as RedditPixelApi['rdt']
44+
;(rdt as any).callQueue = []
45+
window.rdt = rdt
46+
if (options?.id) {
47+
rdt('init', options.id)
48+
rdt('track', 'PageVisit')
49+
}
50+
},
51+
schema: import.meta.dev ? RedditPixelOptions : undefined,
52+
scriptOptions: {
53+
use() {
54+
return { rdt: window.rdt }
55+
},
56+
},
57+
})
58+
}, _options)
59+
}

src/runtime/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import type { GoogleAnalyticsInput } from './registry/google-analytics'
2828
import type { GoogleTagManagerInput } from './registry/google-tag-manager'
2929
import type { UmamiAnalyticsInput } from './registry/umami-analytics'
3030
import type { RybbitAnalyticsInput } from './registry/rybbit-analytics'
31+
import type { RedditPixelInput } from './registry/reddit-pixel'
3132
import type { PayPalInput } from './registry/paypal'
3233
import { object } from '#nuxt-scripts-validator'
3334

@@ -143,6 +144,7 @@ export interface ScriptRegistry {
143144
paypal?: PayPalInput
144145
matomoAnalytics?: MatomoAnalyticsInput
145146
rybbitAnalytics?: RybbitAnalyticsInput
147+
redditPixel?: RedditPixelInput
146148
segment?: SegmentInput
147149
stripe?: StripeInput
148150
xPixel?: XPixelInput

0 commit comments

Comments
 (0)