Skip to content

Commit

Permalink
feat: X pixel support
Browse files Browse the repository at this point in the history
  • Loading branch information
harlan-zw committed Apr 8, 2024
1 parent 6d4c362 commit 2c14d3d
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 43 deletions.
78 changes: 35 additions & 43 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,58 +38,50 @@ npx nuxi@latest module add @nuxt/scripts

To start using Nuxt Scripts, you can use the [useScript](https://unhead.unjs.io/usage/composables/use-script) composable to load your third-party scripts.

### Confetti Example
### Confetti Preview

If you want to get a feel for how the module works, you can load the `js-confetti` library:
If you want to get a preview for how the module works, you can use the registry script for [JS Confetti](https://github.com/loonywizard/js-confetti).

```ts
interface JSConfettiApi { addConfetti: (options?: { emojis: string[] }) => void }
const { addConfetti } = useScript<JSConfettiApi>('https://cdn.jsdelivr.net/npm/js-confetti@latest/dist/js-confetti.browser.js', {
trigger: 'onNuxtReady', // load on onNuxtReady
assetStrategy: 'bundle', // script will be served from your server instead of cdn.jsdelivr.net
use() {
return new window.JSConfetti()
},
// place anywhere, just works in SSR
const { addConfetti } = useScriptConfetti({ version: 'latest' }, {
trigger: 'onNuxtReady' // loads when the browser is idle
})
// useScript is non-blocking, this will run once the script loads
// will be executated on the client when the script is loaded
addConfetti({ emojis: ['🌈', '⚡️', '💥', '', '💫', '🌸'] })
```

## Registry

### Core Scripts

- [useScriptCloudflareWebAnalytics](#cloudflare-web-analytics)
- [useScriptConfetti](#confetti)
- [useScriptFacebookPixel](#facebook-pixel)
- [useScriptXPixel](#x-pixel)
- [useScriptFathomAnalytics](#fathom-analytics)
- [useScriptMatomoAnalytics](#matomo-analytics)
- [useScriptHotjar](#hotjar)
- [useScriptIntercom](#intercom)
- [useScriptSegment](#segment)

[//]: # (TODO - [usePinterestTag](#pinterest-tag))
[//]: # (TODO - [useGoogleAdsConversionTracking](#google-ads-conversion-tracking))
[//]: # (TODO - [useGoogleAdsRemarketing](#google-ads-remarketing))
[//]: # (TODO - [usePlausibleAnalytics](#plausible-analytics))
[//]: # (TODO - [useSimpleAnalytics](#simple-analytics))
[//]: # (TODO - [useUmamiAnalytics](#umami-analytics))

### Module Scripts

- [useScriptGoogleAnalytics](#google-analytics) - Nuxt Third Party Capital
- [useScriptGoogleTagManager](#google-tag-manager) - Nuxt Third Party Capital
- [useScriptGoogleMaps](#google-maps) - Nuxt Third Party Capital
- [useCloudflareTurnstile](#cloudflare-turnstile) - Nuxt Turnstile

## Guides

### Using The Registry (TODO)

The registry is a collection of composables and Nuxt Modules that directly integrate with Nuxt Scripts.

To use a script from the registry, simply use the composable. Consult the
below table for the available scripts.

| Key | Description | Composable | Source |
|----------|------------------------------------------------------------| --- | --- |
| `cloudflare-web-analytics` | Cloudflare Web Analytics | `useScriptCloudflareWebAnalytics` | Core |
| `confetti` | [JS Confetti](https://github.com/loonywizard/js-confetti) | `useScriptCloudflareAnalytics` | Core |
| `facebook-pixel` | Facebook Pixel | `useScriptFacebookPixel` | Core |
| `fathom-analytics` | Fathom Analytics | `useScriptFathomAnalytics` | Core |
| `hotjar` | Hotjar | `useScriptHotjar` | Core |
| `intercom` | Intercom | `useScriptIntercom` | Core |
| `segment` | Segment | `useScriptSegment` | Core |
| `google-analytics` | Google Analytics | `useScriptGoogleAnalytics` | [Nuxt Third Party Capital](https://github.com/nuxt/third-party-capital) |
| `google-tag-manager` | Google Tag Manager | `useScriptGoogleTagManager` | [Nuxt Third Party Capital](https://github.com/nuxt/third-party-capital) |
| `google-maps` | Google Maps | `useScriptGoogleMaps` | [Nuxt Third Party Capital](https://github.com/nuxt/third-party-capital) |
| `cloudflare-turnstile` | CloudFlare Turnstile | `useCloudflareTurnstile` | [Nuxt Cloudflare Turnstile](https://github.com/nuxt-modules/turnstile) |

More coming soon!

[//]: # (TODO | `twitter-pixel` | Twitter Pixel | `useTwitterPixel` |)
[//]: # (TODO | `pinterest-tag` | Pinterest Tag | `usePinterestTag` |)
[//]: # (TODO | `google-ads-conversion-tracking` | Google Ads Conversion Tracking | `useGoogleAdsConversionTracking` |)
[//]: # (TODO | `google-ads-remarketing` | Google Ads Remarketing | `useGoogleAdsRemarketing` |)
[//]: # (TODO | `plausible-analytics` | Plausible Analytics | `usePlausibleAnalytics` |)
[//]: # (TODO | `simple-analytics` | Simple Analytics | `useSimpleAnalytics` |)
[//]: # (TODO | `umami-analytics` | Umami Analytics | `useUmamiAnalytics` |)
[//]: # (TODO | `matomo` | Matomo | `useMatomo` |)

### Loading Scripts Globally
### Loading Custom Scripts Globally

If you prefer a config based approach, you can load scripts globally by defining them in your `nuxt.config.ts`.

Expand Down
4 changes: 4 additions & 0 deletions playground/pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ const thirdParties = [
name: 'Facebook Pixel',
path: '/third-parties/facebook-pixel',
},
{
name: 'X Pixel',
path: '/third-parties/x-pixel',
},
{
name: 'Fathom Analytics',
path: '/third-parties/fathom-analytics',
Expand Down
29 changes: 29 additions & 0 deletions playground/pages/third-parties/x-pixel.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<script lang="ts" setup>
import { useHead, useScriptXPixel } from '#imports'
useHead({
title: 'X Pixel',
})
// composables return the underlying api as a proxy object and a $script with the script state
const { $script, twq } = useScriptXPixel({ id: 'ol7lz' })
// this will be triggered once the script is ready async
function triggerEvent() {
twq('event', ' tw-ol7lz-ol7mb', {
value: 123,
})
}
</script>

<template>
<div>
<ClientOnly>
<div>
status: {{ $script.status }}
</div>
<button @click="triggerEvent">
Trigger Event
</button>
</ClientOnly>
</div>
</template>
6 changes: 6 additions & 0 deletions src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,12 @@ export default defineNuxtModule<ModuleOptions>({
from: resolve('./runtime/registry/facebook-pixel'),
src: 'https://connect.facebook.net/en_US/fbevents.js',
},
{
name: 'useScriptXPixel',
key: 'xPixel',
from: resolve('./runtime/registry/x-pixel'),
src: 'https://static.ads-twitter.com/uwt.js',
},
{
name: 'useScriptFathomAnalytics',
key: 'fathomAnalytics',
Expand Down
73 changes: 73 additions & 0 deletions src/runtime/registry/x-pixel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { type Input, object, string, optional } from 'valibot'

Check failure on line 1 in src/runtime/registry/x-pixel.ts

View workflow job for this annotation

GitHub Actions / ci

Member 'optional' of the import declaration should be sorted alphabetically
import { useScript, validateScriptInputSchema } from '#imports'
import type { NuxtUseScriptIntegrationOptions, NuxtUseScriptOptions } from '#nuxt-scripts'

interface ContentProperties {
content_type?: string | null
content_id?: string | number | null
content_name?: string | null
content_price?: string | number | null
num_items?: string | number | null
content_group_id?: string | number | null
}

interface EventObjectProperties {
// this is the same as Twitter events
value?: string | number | null
currency?: string | null
conversion_id?: string | number | null
email_address?: string | null
phone_number?: string | null
contents: ContentProperties[]
}

type TwqFns =
((event: 'event', eventId: string, data?: EventObjectProperties) => void)
& ((event: 'config', id: string) => void)
& ((event: string, ...params: any[]) => void)

export interface XPixelApi {
twq: TwqFns & {
loaded: boolean
version: string
queue: any[]
}
}

declare global {
interface Window extends XPixelApi {
}
}

export const XPixelOptions = object({
id: string(),
version: optional(string()),
})
export type XPixelInput = Input<typeof XPixelOptions>

export function useScriptXPixel<T extends XPixelApi>(options?: XPixelInput, _scriptOptions?: NuxtUseScriptIntegrationOptions) {
const scriptOptions: NuxtUseScriptOptions<T> = _scriptOptions || {}
scriptOptions.beforeInit = () => {
import.meta.dev && validateScriptInputSchema(XPixelOptions, options)

if (import.meta.client) {
const s = window.twq = function (...params: any[]) {
s.exe ? s.exe(s, ...params) : s.queue.push(params)
}
s.version = options.version || '1.1'
s.queue = [
['config', options.id],
]
}
_scriptOptions?.beforeInit?.()
}
return useScript<T>({
key: 'xPixel',
src: 'https://static.ads-twitter.com/uwt.js',
}, {
...scriptOptions,
use() {
return { twq: window.twq }
},
})
}

0 comments on commit 2c14d3d

Please sign in to comment.