-
Notifications
You must be signed in to change notification settings - Fork 0
feat(components): add MetaPixel component with event tracking #5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,91 @@ | ||||||||||||||||||||||||||||||||||
| 'use client' | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| import Script from 'next/script' | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| export interface MetaPixelProps { | ||||||||||||||||||||||||||||||||||
| /** Meta (Facebook) Pixel ID (e.g., '1275259234564588') */ | ||||||||||||||||||||||||||||||||||
| pixelId: string | ||||||||||||||||||||||||||||||||||
| /** Load strategy (default: 'afterInteractive') */ | ||||||||||||||||||||||||||||||||||
| strategy?: 'afterInteractive' | 'lazyOnload' | 'beforeInteractive' | ||||||||||||||||||||||||||||||||||
| /** Disable automatic PageView tracking (default: false) */ | ||||||||||||||||||||||||||||||||||
| disablePageView?: boolean | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| const PIXEL_ID_PATTERN = /^\d{15,16}$/ | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| export function MetaPixel({ | ||||||||||||||||||||||||||||||||||
| pixelId, | ||||||||||||||||||||||||||||||||||
| strategy = 'afterInteractive', | ||||||||||||||||||||||||||||||||||
| disablePageView = false | ||||||||||||||||||||||||||||||||||
| }: MetaPixelProps) { | ||||||||||||||||||||||||||||||||||
| // Don't render if no pixel ID or invalid format | ||||||||||||||||||||||||||||||||||
| if (!pixelId || !PIXEL_ID_PATTERN.test(pixelId)) { | ||||||||||||||||||||||||||||||||||
| return null | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| const pageViewScript = disablePageView ? '' : `fbq('track', 'PageView');` | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||
| <> | ||||||||||||||||||||||||||||||||||
| <Script id="meta-pixel-init" strategy={strategy}> | ||||||||||||||||||||||||||||||||||
| {` | ||||||||||||||||||||||||||||||||||
| !function(f,b,e,v,n,t,s) | ||||||||||||||||||||||||||||||||||
| {if(f.fbq)return;n=f.fbq=function(){n.callMethod? | ||||||||||||||||||||||||||||||||||
| n.callMethod.apply(n,arguments):n.queue.push(arguments)}; | ||||||||||||||||||||||||||||||||||
| if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0'; | ||||||||||||||||||||||||||||||||||
| n.queue=[];t=b.createElement(e);t.async=!0; | ||||||||||||||||||||||||||||||||||
| t.src=v;s=b.getElementsByTagName(e)[0]; | ||||||||||||||||||||||||||||||||||
| s.parentNode.insertBefore(t,s)}(window, document,'script', | ||||||||||||||||||||||||||||||||||
| 'https://connect.facebook.net/en_US/fbevents.js'); | ||||||||||||||||||||||||||||||||||
| fbq('init', '${pixelId}'); | ||||||||||||||||||||||||||||||||||
| ${pageViewScript} | ||||||||||||||||||||||||||||||||||
| `} | ||||||||||||||||||||||||||||||||||
| </Script> | ||||||||||||||||||||||||||||||||||
| <noscript> | ||||||||||||||||||||||||||||||||||
| <img | ||||||||||||||||||||||||||||||||||
| height="1" | ||||||||||||||||||||||||||||||||||
| width="1" | ||||||||||||||||||||||||||||||||||
| style={{ display: 'none' }} | ||||||||||||||||||||||||||||||||||
| src={`https://www.facebook.com/tr?id=${pixelId}&ev=PageView&noscript=1`} | ||||||||||||||||||||||||||||||||||
| alt="" | ||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||
| </noscript> | ||||||||||||||||||||||||||||||||||
| </> | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||
| * Track a Meta Pixel event | ||||||||||||||||||||||||||||||||||
| * Call this function to track custom events after the pixel is loaded | ||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||
| * @example | ||||||||||||||||||||||||||||||||||
| * // Standard event | ||||||||||||||||||||||||||||||||||
| * trackMetaEvent('Lead') | ||||||||||||||||||||||||||||||||||
| * trackMetaEvent('Purchase', { value: 99.99, currency: 'USD' }) | ||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||
| * // Custom event | ||||||||||||||||||||||||||||||||||
| * trackMetaEvent('CustomEvent', { custom_param: 'value' }) | ||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+59
to
+69
|
||||||||||||||||||||||||||||||||||
| export function trackMetaEvent( | ||||||||||||||||||||||||||||||||||
| eventName: string, | ||||||||||||||||||||||||||||||||||
| params?: Record<string, unknown> | ||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||
| if (typeof window !== 'undefined' && typeof window.fbq === 'function') { | ||||||||||||||||||||||||||||||||||
| if (params) { | ||||||||||||||||||||||||||||||||||
| window.fbq('track', eventName, params) | ||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||
| window.fbq('track', eventName) | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
| if (params) { | |
| window.fbq('track', eventName, params) | |
| } else { | |
| window.fbq('track', eventName) | |
| } | |
| window.fbq('track', eventName, params) |
Copilot
AI
Feb 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The PR description mentions a trackMetaCustomEvent() helper function, but this function does not exist in the code. Only trackMetaEvent() is implemented. Either the PR description should be updated to remove this reference, or the function should be added to match the documentation. Note that Facebook Pixel distinguishes between standard events (using fbq('track', eventName)) and custom events (using fbq('trackCustom', eventName)).
Copilot
AI
Feb 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The TypeScript Window interface extension for fbq is incomplete. The Facebook Pixel fbq function supports multiple actions ('init', 'track', 'trackCustom', etc.), but the current type definition only supports 'track'. This will cause TypeScript errors when using fbq('init', pixelId) as seen on line 40. The function signature should be updated to support multiple overloads or use a more flexible type that accommodates all fbq actions.
| fbq: ( | |
| action: string, | |
| eventName: string, | |
| params?: Record<string, unknown> | |
| ) => void | |
| fbq( | |
| action: 'init', | |
| pixelId: string, | |
| options?: Record<string, unknown> | |
| ): void | |
| fbq( | |
| action: 'track' | 'trackCustom', | |
| eventName: string, | |
| params?: Record<string, unknown> | |
| ): void | |
| fbq(action: string, ...args: unknown[]): void |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| export * from './BasicPageLayout' | ||
| export * from './GoogleAnalytics' | ||
| export * from './MetaPixel' | ||
| export * from './ScrollToTop' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When disablePageView is true, the JavaScript code skips PageView tracking, but the noscript fallback image still includes 'ev=PageView' in the URL. This creates inconsistent behavior between JavaScript and non-JavaScript environments. The noscript fallback should respect the disablePageView prop and conditionally render based on its value, or omit the 'ev' parameter when PageView tracking is disabled.