Skip to content
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

How to use this library with next.js #53

Open
icecold21 opened this issue Jul 22, 2020 · 24 comments
Open

How to use this library with next.js #53

icecold21 opened this issue Jul 22, 2020 · 24 comments

Comments

@icecold21
Copy link

Hi, i was wondering how to use this library with next.js.

@charinduedirisuriya
Copy link

I am wondering the same.

@lineape
Copy link

lineape commented Aug 12, 2020

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();
        });
      });
  }
}

@fikip
Copy link

fikip commented Aug 27, 2020

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.

@ScottSmith95
Copy link

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>
      //…
    </>
  );
}

@omar-dulaimi
Copy link

omar-dulaimi commented Sep 21, 2020

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

@ashconnell
Copy link

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.

@vincentleeuwen
Copy link

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();
};

@LeOndaz
Copy link

LeOndaz commented Jan 25, 2021

Can anyone tell me why the solution of importing directly inside componentDidMount works?

@lineape
Copy link

lineape commented Jan 25, 2021

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.

@LeOndaz
Copy link

LeOndaz commented Jan 25, 2021

@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?

@lineape
Copy link

lineape commented Jan 25, 2021

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

@LeOndaz
Copy link

LeOndaz commented Jan 25, 2021

Yea yea, thanks, appreciated

@DiegoMcDipster
Copy link

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?

@lineape
Copy link

lineape commented Jan 29, 2021

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.

@DiegoMcDipster
Copy link

@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!

@tsm20
Copy link

tsm20 commented Mar 31, 2021

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:
Screenshot_86

Dev and Production:
Screenshot_87

@NishargShah
Copy link

Use https://github.com/vercel/next.js/tree/canary/examples/with-facebook-pixel

@diogosilva95
Copy link

diogosilva95 commented May 11, 2021

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>

@syriail
Copy link

syriail commented May 27, 2021

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:

  1. We cannot reinitialise pixel when user logs in
  2. Putting a script like that may block rendering and then increase FCP.
    So I came up with this solution using react context
    Create a context file with the following code, then you can call recordFBEvent whenever you want even in a function like "purchase"
    Also it will reinitialise Pixel when user logs in.
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

@heet-solutelabs
Copy link

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:
Screenshot_86

Dev and Production:
Screenshot_87

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

@Guneetgstar
Copy link

Guneetgstar commented Jul 6, 2021

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.

@cnscorpions
Copy link

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?

@cnscorpions
Copy link

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.

@thallysondias
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests