Open icecold21 opened 4 years ago
I am wondering the same.
With the newest version, the only way I've found that works is to use a dynamic import because they are using window
directly, and that doesn't work on server-side render, so it won't build.
// in your _app.tsx
import NextApp from 'next/app';
import Router from 'next/router';
export default class App extends NextApp {
componentDidMount() {
import('react-facebook-pixel')
.then((x) => x.default)
.then((ReactPixel) => {
ReactPixel.init('your-pixel-id');
ReactPixel.pageView();
Router.events.on('routeChangeComplete', () => {
ReactPixel.pageView();
});
});
}
}
Here's what I'm doing in a PixelProvider
component that I wrap the rest of the app with in _app.tsx
. Seems to work without any issues.
import React, { FC, useEffect } from "react";
import { useRouter } from "next/router";
const PixelProvider: FC = ({ children }) => {
const router = useRouter();
useEffect(() => {
const shouldTrack =
!isLocal() && !isDev() && isBrowser() && !window.FB_INITIALIZED;
if (shouldTrack) {
import("react-facebook-pixel")
.then((module) => module.default)
.then((ReactPixel) => {
ReactPixel.init("3208084489267232");
ReactPixel.pageView();
router.events.on("routeChangeComplete", () => {
ReactPixel.pageView();
});
});
}
}, []);
return <>{children}</>;
};
export default PixelProvider;
const isBrowser = () => typeof window !== "undefined";
const isLocal = () => location.hostname === "localhost";
const isDev = () => process.env.NODE_ENV !== "production";
Hope this helps.
I've made a little helper component that I place in my <App>
component.
function FacebookPixel() {
React.useEffect(() => {
import("react-facebook-pixel")
.then((x) => x.default)
.then((ReactPixel) => {
ReactPixel.init(constants.siteMeta.FacebookPixelID);
ReactPixel.pageView();
Router.events.on("routeChangeComplete", () => {
ReactPixel.pageView();
});
});
});
return null;
}
export default function App({ Component, pageProps }) {
return (
<>
<Head>
<meta charSet="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, viewport-fit=cover"
/>
</Head>
<FacebookPixel />
//…
<main className="routesContainer">
<Component siteData={siteData} {...pageProps} />
</main>
//…
</>
);
}
I tried the samples above but they really don't work. The library should accept an ssr option to control if it's immediately invoked or not. This forces us to use the 1 year old buggy version of 0.1.3
These were great suggestions but I noticed that during dev the router event listener isn't being destroyed so each time you make a code change it just stacks event bindings (you can see this if you add console logs to the code).
I ended up with this:
import React, { useEffect } from 'react'
import { useRouter } from 'next/router'
const FACEBOOK_PIXEL_ID = process.env.NEXT_PUBLIC_FACEBOOK_PIXEL_ID
export const FacebookPixel = function () {
const router = useRouter()
useEffect(() => {
if (!FACEBOOK_PIXEL_ID) return
let fb
function onRouteChange(url) {
fb.pageView()
}
import('react-facebook-pixel')
.then(module => (fb = module.default))
.then(() => {
fb.init(FACEBOOK_PIXEL_ID, {
autoConfig: true,
debug: true,
})
fb.pageView()
})
router.events.on('routeChangeComplete', onRouteChange)
return () => {
router.events.off('routeChangeComplete', onRouteChange)
}
}, [])
return null
}
It's not perfect because there's a chance the router event triggers before the module loads but I couldn't reproduce it so kept it simple.
@omar-dulaimi this works absolutely fine and I suspect you took a wrong turn somewhere if this didn't work out for you.
Great solutions worked for me! If you're using next you should have access to async / await
which makes this syntax a bit nicer imho:
export const trackFbPageView = async () => {
const { default: ReactPixel } = await import('react-facebook-pixel');
ReactPixel.init(FB_PIXEL_ID, advancedMatching, options);
ReactPixel.pageView();
};
Can anyone tell me why the solution of importing directly inside componentDidMount works?
It's because componentDidMount and useEffect aren't run server-side. If the node.js server doesn't evaluate the contents of the react-facebook-pixel module, then it doesn't hit the code that uses window
, which is what's causing the issue.
@lineape Thanks for your fast response, I understand that both run on the Client, however, if both run on the client, does this mean the react facebook pixel just mentions window
directly in it's source code which means that the error occurs at import time?
Yes.
This line would throw with an uncaught ReferenceError
Edit: on second thought, it might not, it would only throw when the init method is called. It's been a few months since I looked into this and that was the issue at the time. For all I know it works now :P
Yea yea, thanks, appreciated
I'm pretty new to Nextjs (and to react for that matter). Do you have suggestions on how I could combine this with a cookie consent box? Is that even possible if it's SSG?
I'm pretty new to Nextjs (and to react for that matter). Do you have suggestions on how I could combine this with a cookie consent box? Is that even possible if it's SSG?
Combine in what way? Like, only track page views if they've given cookie consent? Pretty easy.
The react-cookie-consent module for example has a getCookieConsentValue method, so a really naive implementation could be something like this:
Somewhere you'd have something like this for getting their consent
import CookieConsent from 'react-cookie-consent';
function Page() {
return (
<div>
<h1>Hello World</h1>
<CookieConsent location="bottom" />
</div>
);
}
And then you'd have your page tracking somewhere in your _app.tsx
import { getCookieConsentValue } from 'react-cookie-consent';
function useFacebookPageTracking() {
useEffect(() => {
if (getCookieConsentValue() !== 'true') return;
// then do the tracking
}, [getCookieConsentValue()]);
}
Note: I have tested none of this code, this is just an example of how to tackle it.
@lineape thanks for the guidance! I got it working using react-cookie-consent and the code provided above by @ashconnell. Really appreciate you guys taking the time to share your knowledge!
Hello all, Im trying to use this library in Nextjs and i followed @vincentleeuwen approach. And i also tried using the default scripts provided on pixel documention. I was able to config Pixel properly in localhost but when i deployed this version it says that detects the pixel but it shows a warning in PageView
localhost:
Dev and Production:
This way worked for me. Create a _document.js on your pages folder like this one and add the pixel to a script tag with dangerously innerHtml.
<Head>
{/* Facebook Pixel Code */}
<script
dangerouslySetInnerHTML={{
__html: `!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', 'YOURID');
fbq('track', 'PageView');`,
}}
/>
{/* End Facebook Pixel Code */}
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
Thank you @ashconnell , your code was very useful. The problem with it, that it only records PageView event. Also thank you @NishargShah for pointing to Next Js example. Which handles the early mentioned problem, but it has two problems:
import { createContext, useState, useEffect } from "react"
import { connect } from "react-redux"
import EnvironmentConfig from "../env-config"
const FACEBOOK_PIXEL_ID = process.env.NEXT_PUBLIC_FACEBOOK_PIXEL_ID
const ReactPixelContext = createContext({
recordeFBEvent: function (event, data) {},
})
const mapStateToProps = (state) => ({
currentCustomer: state.customer.currentCustomer,
})
export const ReactPixelContextProvider = connect(mapStateToProps)((props) => {
const [reactPixel, setReactPixel] = useState()
let beingInitialized = false
const init = (customer, event, data) => {
if (!FACEBOOK_PIXEL_ID) return
let fb
if ((!reactPixel || customer) && !beingInitialized) {
beingInitialized = true
import("react-facebook-pixel")
.then((module) => (fb = module.default))
.then(() => {
let adavancedMatching = {}
if (customer) {
if (customer.email) {
adavancedMatching.em = customer.email
}
if (customer.mobileNumber) {
adavancedMatching.ph = customer.mobileNumber
}
}
const options = {
autoConfig: true,
debug: EnvironmentConfig.current !== "prod",
}
console.log("initialize with matching: ")
console.log(adavancedMatching)
fb.init(FACEBOOK_PIXEL_ID, adavancedMatching, options)
if (event && data) {
console.log(event)
console.log(data)
fb.track(event, data)
}
setReactPixel(fb)
beingInitialized = false
})
}
}
useEffect(() => {
if (!beingInitialized) {
init()
}
}, [])
useEffect(() => {
console.log("should be re-initialized with custoemr")
console.log(props.currentCustomer)
if (!beingInitialized) {
init(props.currentCustomer)
}
}, [props.currentCustomer])
const recordEventHandler = (event, data) => {
if (!reactPixel) {
console.log(event)
console.log(data)
init(null, event, data)
return
}
console.log(event)
console.log(data)
reactPixel.track(event, data)
}
const context = { recordeFBEvent: recordEventHandler }
return (
<ReactPixelContext.Provider value={context}>
{props.children}
</ReactPixelContext.Provider>
)
})
export default ReactPixelContext
It might not be perfect, so I'm looking forward to your comments
Hello all, Im trying to use this library in Nextjs and i followed @vincentleeuwen approach. And i also tried using the default scripts provided on pixel documention. I was able to config Pixel properly in localhost but when i deployed this version it says that detects the pixel but it shows a warning in PageView
localhost:
Dev and Production:
any update on this i am facing same issue? i am using this reference https://github.com/vercel/next.js/tree/canary/examples/with-facebook-pixel but some how i am facing same issue as mention @tsm20
Following @vincentleeuwen the below code did work for me, though Facebook Pixel Helper still can't identify if my app has Pixel configured or not but the console logs proves it's working:
useEffect(async () => {
const { default: ReactPixel } = await import('react-facebook-pixel');
ReactPixel.init(FB_PIXEL, null, {
autoConfig: true,
debug: true,
});
ReactPixel.pageView();
ReactPixel.track("ViewContent")
});
Also, do pause your ad-blockers if any to see pixel work.
The problem is that It only records PageView events for all the above workarounds, and I'd like to track 'purchase' events in an online store created by Next.js. @syriail Thank you for your solution, but it is very complicated for importing react-redux and context 😂. Is there an easier workaround?
My workaround for purchase event is as follows:
// in utils/fb.js
export const trackFbPageView = async () => {
const { default: ReactPixel } = await import('react-facebook-pixel');
ReactPixel.init('*****') // facebookPixelId
ReactPixel.pageView();
}
export const trackFbPurchase = async () => {
const { default: ReactPixel } = await import('react-facebook-pixel');
ReactPixel.init('*****') // facebookPixelId
ReactPixel.track('Purchase', {currency: "USD", value: 29.9})
}
I am not sure whether reinitializing ReactPixel has a bad impact on my website performance.
Hello guys, my solution working:
Create a component:
components/pixel-events.tsx
"use client";
import React, { useEffect } from "react";
import { usePathname, useSearchParams } from "next/navigation";
//FACEBOOK INIT
export const FacebookPixelEvents: React.FC = () => {
const pathname = usePathname();
const searchParams = useSearchParams();
useEffect(() => {
import("react-facebook-pixel")
.then((x) => x.default)
.then((ReactPixel) => {
ReactPixel.init("xxxxxx"); //don't forget to change this
ReactPixel.pageView();
});
}, [pathname, searchParams]);
return null;
};
//CUSTOM EVENTS
export const CompleteRegistration: React.FC = () => {
const pathname = usePathname();
const searchParams = useSearchParams();
useEffect(() => {
import("react-facebook-pixel")
.then((x) => x.default)
.then((ReactPixel) => {
ReactPixel.track('CompleteRegistration')
});
}, [pathname, searchParams]);
return null;
};
On layout.tsx add you component after {children}, to fire "pageview" event on all pages.
....
{children}
<Suspense fallback={null}>
<FacebookPixelEvents />
</Suspense>
....
For custom Events, just call the function. Example, i call "CompleteRegistration" when user come the "thank-you" page:
<Suspense fallback={null}>
<CompleteRegistration />
</Suspense>
check all events here: https://developers.facebook.com/docs/meta-pixel/reference#standard-events
Hi, i was wondering how to use this library with next.js.