zsajjad / react-facebook-pixel

React JS wrapper for Facebook's Pixel
MIT License
233 stars 86 forks source link

"react-facebook-pixel" doesn't work with Next.js. "ReferenceError: window is not defined" #65

Closed estebandlp closed 3 years ago

estebandlp commented 3 years ago

When I install and try to use the library with Next.js, and Init the Pixel, then I have the following error: "ReferenceError: window is not defined"

This error is in fb-pixel.js and is fixed adding: if (typeof window !== "undefined") at the beginning of the script.

This modification could generate another issue?

carlmagumpara commented 3 years ago

same here problem with ssr

jonrrivera commented 3 years ago

bump

superapplejuice commented 3 years ago

There are many solutions in issue #53. Instead of doing a direct import like import ReactPixel from 'react-facebook-pixel, you need to dynamically import it:

useEffect(() => {
  import('react-facebook-pixel')
    .then(module => module.default)
    .then(ReactPixel => {
      ReactPixel.init('your-pixel-code)
      ReactPixel.pageView()
    })
}, []
dlong500 commented 3 years ago

@superapplejuice While it may be true that there are workarounds, it would be extremely easy for this module to make itself SSR friendly (as it already was before v1.0). There can be build system side effects or other situations where dynamic imports won't necessarily work (or at least are far more complicated). It would be such an easy fix to make the module a no-op if a browser environment is not available.

Since a user tracking system like pixel is by definition "client-side" there is no point in making developers have to jump through hoops to force the module to not load on the server.

I'm more concerned that @zsajjad hasn't weighed in on the issue yet. Of course we can always fork the module since it would be an easy fix, but it is highly preferable for the original maintainers (who may have other features/enhancements in the works) to implement a fix that would make the library much easier to use. Maybe @zsajjad is busy; I know maintaining open source libraries can be a burden. I'm certainly thankful for the code--just hoping we can get it back to being SSR friendly.

zsajjad commented 3 years ago

Hi sincere apologies for the delayed response. I would highly appreciate any PR or good suggestions on fixing this issue

dlong500 commented 3 years ago

@zsajjad Thanks for the response. Can you explain why the change was made in eb70fa4 (patch for existing integration)? This is the cause of the SSR issues:

let initialized = !!window.fbq;

Is there a specific reason why the initialized variable needs to know if the fbq object exists before the init method is called?

It would seem like a pretty easy fix would simply be to change that line to:

let initialized = (typeof window !== 'undefined') && !!window.fbq;

I see that @mimcz appears to have already submitted a pull request with essentially this very change.

zsajjad commented 3 years ago

I have merged that MR. Will release the updated version in a while. Thanks for taking this up.

fvpDev commented 3 years ago

Hi. When will this be released to npmjs.com? Currently, it is version 1.0.3 there and was last published 5 months ago.

zsajjad commented 3 years ago

New version is live

iaarnio commented 3 years ago

@zsajjad This still fails for Gatsby SSR build. Using latest version 1.0.4.

  WebpackError: ReferenceError: window is not defined

  - fb-pixel.js:1
    node_modules/react-facebook-pixel/dist/fb-pixel.js:1:201

How to reproduce:

npx gatsby new my-default-starter https://github.com/gatsbyjs/gatsby-starter-default
cd my-default-starter
npm i react-facebook-pixel 
# + react-facebook-pixel@1.0.4
npm i prop-types # due Can't resolve 'prop-types' in ... 'gatsby-react-router-scroll'
# 'npx gatsby build' still works here 
# add import for react-facebook-pixel to e.g. src/pages/index.js
npx gatsby build #fails now
dlong500 commented 3 years ago

@iaarnio it appears you are using the pre-built module in the dist folder that for some reason hasn't been updated since v1.0.1. I haven't tested the new version yet in my setup so I can't comment yet on whether the issue still appears on my side.

Edited - see post below

dlong500 commented 3 years ago

@zsajjad I've tested the new release and have the same problem @iaarnio has. Even when bundling with webpack the library is using the dist folder that has been committed to the repo (which is still on v1.0.1).

~I think the main parameter in package.json should point to src/index.js.~ This would allow bundlers like webpack to use the actual source files when building a client app. Including a UMD build in a dist folder is fine, but that is really only for users who don't build their apps using bundlers like webpack, and these users should specify the full path to the file. If the main parameter points to a pre-built dist file then we lose the benefits of webpack processing when building the app client side.

dlong500 commented 3 years ago

I've dug into this a bit more and I think what is missing here is the transpiled source (usually put in a lib folder). Since we're in a transitional period with ES source code in the context of package managers a fairly conventional process is to provide the raw (untranspiled) code in src, a transpiled (ES5) version in lib, and a UMD build in dist. The package.json main field in this scenario points to the transpiled version in lib allowing bundlers like webpack to access the non-minified transpiled code. Those who need a UMD build can access it directly via the dist folder.

When I get a bit of free time I'll submit a PR that adds the lib generation, changes the main field, and also adds a prepare script for use before publishing to NPM that calls the build scripts.

Edit - I've now sent a PR with the relevant fixes

Sundaram1911 commented 6 months ago

import ReactPixel from 'react-facebook-pixel';

Error: window is not defined

why its not working with next js in the global js file or anywhere else also ? previously it was working with react current version :^1.0.4 (react-facebook-fixel);

facutolozadev commented 3 months ago

I had this same problem.

"use client";
import React, { useEffect } from "react";
import { usePathname, useSearchParams } from "next/navigation";
import ReactPixel from 'react-facebook-pixel';

export const FacebookPixelEvents: React.FC = () => {
  const pathname = usePathname();
  const searchParams = useSearchParams();

  useEffect(() => {
    import("react-facebook-pixel")
      .then((x) => x.default)
      .then((ReactPixel) => {
        ReactPixel.init(`${process.env.NEXT_PUBLIC_FACEBOOK_PIXEL_ID}`); 
        ReactPixel.pageView();
      });
    ReactPixel.init(`${process.env.NEXT_PUBLIC_FACEBOOK_PIXEL_ID}`);
    ReactPixel.pageView();
  }, [pathname, searchParams]);

  return null;
};

export const fbPixelAddToCart = async () => {
  ReactPixel.fbq('track', 'AddToCart');

}

export const fbPixelInitiateCheckout = async () => {
  ReactPixel.fbq('track', 'InitiateCheckout');
}

This line was throwing the error: "window is not defined":

import ReactPixel from 'react-facebook-pixel';

So i've made some conditional imports:

"use client";
import React, { useEffect, useState } from "react";
import { usePathname, useSearchParams } from "next/navigation";

let ReactPixel: any = null;

if (typeof window !== 'undefined') {
  import('react-facebook-pixel')
    .then((x) => x.default)
    .then((currReactPixel) => {
      ReactPixel = currReactPixel;
    })
}

export const FacebookPixelEvents: React.FC = () => {
  const pathname = usePathname();
  const searchParams = useSearchParams();

  useEffect(() => {
    if(!ReactPixel) {
      import('react-facebook-pixel')
      .then((x) => x.default)
      .then((currReactPixel) => {
        ReactPixel = currReactPixel;
      })
      ReactPixel.init(`${process.env.NEXT_PUBLIC_FACEBOOK_PIXEL_ID}`);
      ReactPixel.pageView();
    } else {
      ReactPixel.init(`${process.env.NEXT_PUBLIC_FACEBOOK_PIXEL_ID}`);
      ReactPixel.pageView();
    }
  }, [pathname, searchParams]);

  return null;
};

export const fbPixelAddToCart = async () => {
  if(ReactPixel) ReactPixel.fbq('track', 'AddToCart');
}

export const fbPixelInitiateCheckout = async () => {
  if(ReactPixel) ReactPixel.fbq('track', 'InitiateCheckout');
}

Do a conditional import only when the window type is not undefined, and on the first render I had the problem that the React pixel was sometimes null, so in useEffect I also did a conditional import. (pedilo)