Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 145 additions & 0 deletions docs/content/scripts/tracking/reddit-pixel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
---
title: Reddit Pixel
description: Use Reddit Pixel in your Nuxt app.
links:
- label: Source
icon: i-simple-icons-github
to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/reddit-pixel.ts
size: xs
---

[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.

Nuxt Scripts provides a registry script composable `useScriptRedditPixel` to easily integrate Reddit Pixel in your Nuxt app.

### Nuxt Config Setup

The simplest way to load Reddit Pixel globally in your Nuxt App is to use Nuxt config. Alternatively you can directly
use the [useScriptRedditPixel](#useScriptRedditPixel) composable.

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
disable the script in development.

::code-group

```ts [Always enabled]
export default defineNuxtConfig({
scripts: {
registry: {
redditPixel: {
id: 'YOUR_ID'
}
}
}
})
```

```ts [Production only]
export default defineNuxtConfig({
$production: {
scripts: {
registry: {
redditPixel: {
id: 'YOUR_ID',
}
}
}
}
})
```

::

#### With Environment Variables

If you prefer to configure your id using environment variables.

```ts [nuxt.config.ts]
export default defineNuxtConfig({
scripts: {
registry: {
redditPixel: true,
}
},
// you need to provide a runtime config to access the environment variables
runtimeConfig: {
public: {
scripts: {
redditPixel: {
id: '', // NUXT_PUBLIC_SCRIPTS_REDDIT_PIXEL_ID
},
},
},
},
})
```

```text [.env]
NUXT_PUBLIC_SCRIPTS_REDDIT_PIXEL_ID=<YOUR_ID>
```

## useScriptRedditPixel

The `useScriptRedditPixel` composable lets you have fine-grain control over when and how Reddit Pixel is loaded on your site.

```ts
const { proxy } = useScriptRedditPixel({
id: 'YOUR_ID'
})
// example
proxy.rdt('track', 'Lead')
```

Please follow the [Registry Scripts](/docs/guides/registry-scripts) guide to learn more about advanced usage.

### RedditPixelApi

```ts
export interface RedditPixelApi {
rdt: RdtFns & {
sendEvent: (rdt: RedditPixelApi['rdt'], args: unknown[]) => void
callQueue: unknown[]
}
}
type RdtFns
= & ((event: 'init', id: string) => void)
& ((event: 'track', eventName: string) => void)
```

### Config Schema

You must provide the options when setting up the script for the first time.

```ts
export const RedditPixelOptions = object({
id: string(),
})
```

## Example

Using Reddit Pixel only in production while using `rdt` to send a tracking event.

::code-group

```vue [TrackingButton.vue]
<script setup lang="ts">
const { proxy } = useScriptRedditPixel()

// noop in development, ssr
// just works in production, client
function trackConversion() {
proxy.rdt('track', 'Lead')
}
</script>

<template>
<div>
<button @click="trackConversion">
Track Conversion
</button>
</div>
</template>
```

::
4 changes: 4 additions & 0 deletions playground/pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ const thirdParties = [
name: 'X Pixel',
path: '/third-parties/x-pixel/nuxt-scripts',
},
{
name: 'Reddit Pixel',
path: '/third-parties/reddit-pixel/nuxt-scripts',
},
{
name: 'Google Adsense',
path: '/third-parties/google-adsense/nuxt-scripts',
Expand Down
22 changes: 22 additions & 0 deletions playground/pages/third-parties/reddit-pixel/default.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<script lang="ts" setup>
import { useHead } from '#imports'

useHead({
script: [
{ 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')` },
],
})

function triggerEvent() {
window.rdt('init', 'YOUR_PIXEL_ID')
window.rdt('track', 'PageVisit')
}
</script>

<template>
<div>
<UButton @click="() => triggerEvent()">
Trigger Event
</UButton>
</div>
</template>
27 changes: 27 additions & 0 deletions playground/pages/third-parties/reddit-pixel/nuxt-scripts.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<script lang="ts" setup>
import { useHead, useScriptRedditPixel } from '#imports'

useHead({
title: 'Reddit',
})

// composables return the underlying api as a proxy object and the script state
const { status, rdt } = useScriptRedditPixel({ id: 'YOUR_PIXEL_ID' })
// this will be triggered once the script is ready async
function triggerEvent() {
rdt('track', 'PageVisit')
}
</script>

<template>
<div>
<ClientOnly>
<div>
status: {{ status }}
</div>
<UButton @click="triggerEvent">
Trigger Event
</UButton>
</ClientOnly>
</div>
</template>
10 changes: 10 additions & 0 deletions src/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,16 @@ export async function registry(resolve?: (path: string, opts?: ResolvePathOption
from: await resolve('./runtime/registry/snapchat-pixel'),
},
},
{
label: 'Reddit Pixel',
src: 'https://www.redditstatic.com/ads/pixel.js',
category: 'tracking',
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>`,
import: {
name: 'useScriptRedditPixel',
from: await resolve('./runtime/registry/reddit-pixel'),
},
},
// ads
{
label: 'Google Adsense',
Expand Down
59 changes: 59 additions & 0 deletions src/runtime/registry/reddit-pixel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import type { UseScriptInput } from '@unhead/vue'
import { useRegistryScript } from '../utils'
import { object, string } from '#nuxt-scripts-validator'
import type { RegistryScriptInput } from '#nuxt-scripts/types'

type RdtFns
= & ((event: 'init', id: string) => void)
& ((event: 'track', eventName: string) => void)

export interface RedditPixelApi {
rdt: RdtFns & {
sendEvent: (rdt: RedditPixelApi['rdt'], args: unknown[]) => void
callQueue: unknown[]
}
}

declare global {
interface Window extends RedditPixelApi {}
}

export const RedditPixelOptions = object({
id: string(),
})
export type RedditPixelInput = RegistryScriptInput<typeof RedditPixelOptions, true, false, false>

export function useScriptRedditPixel<T extends RedditPixelApi>(_options?: RedditPixelInput) {
return useRegistryScript<T, typeof RedditPixelOptions>('redditPixel', (options) => {
return ({
scriptInput: {
src: 'https://www.redditstatic.com/ads/pixel.js',
async: true,
} as UseScriptInput,
clientInit: import.meta.server
? undefined
: () => {
const rdt = function (...args: unknown[]) {
if ((rdt as any).sendEvent) {
(rdt as any).sendEvent(rdt, args)
}
else {
(rdt as any).callQueue.push(args)
}
} as RedditPixelApi['rdt']
;(rdt as any).callQueue = []
window.rdt = rdt
if (options?.id) {
rdt('init', options.id)
rdt('track', 'PageVisit')
}
},
schema: import.meta.dev ? RedditPixelOptions : undefined,
scriptOptions: {
use() {
return { rdt: window.rdt }
},
},
})
}, _options)
}
2 changes: 2 additions & 0 deletions src/runtime/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import type { GoogleAnalyticsInput } from './registry/google-analytics'
import type { GoogleTagManagerInput } from './registry/google-tag-manager'
import type { UmamiAnalyticsInput } from './registry/umami-analytics'
import type { RybbitAnalyticsInput } from './registry/rybbit-analytics'
import type { RedditPixelInput } from './registry/reddit-pixel'
import type { PayPalInput } from './registry/paypal'
import { object } from '#nuxt-scripts-validator'

Expand Down Expand Up @@ -143,6 +144,7 @@ export interface ScriptRegistry {
paypal?: PayPalInput
matomoAnalytics?: MatomoAnalyticsInput
rybbitAnalytics?: RybbitAnalyticsInput
redditPixel?: RedditPixelInput
segment?: SegmentInput
stripe?: StripeInput
xPixel?: XPixelInput
Expand Down
Loading