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

Facebook pixel #550

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions facebook-pixel/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules

/.cache
.env
35 changes: 35 additions & 0 deletions facebook-pixel/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# TODO: Title of Example

Setup Facebook Pixel with Remix. Inspired by the ```google-analytics``` example, we will do some adjustments to make sending Pixel events work with Remix.



## Preview

Open this example on [CodeSandbox](https://codesandbox.com):

<!-- TODO: update this link to the path for your example: -->

[![Open in CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/github/remix-run/examples/tree/main/__template)

## Example

- Check ```app/utils/pixel.client.ts```. its a simple wrapper for the pixel's ```fbq``` function.

- Check [app/root.tsx](./app/root.tsx) to see how the Facebook Pixel script is added.

We use ```root.stx``` to fire initial events and the default ```PageView``` event. Every url visited in the app will fire a ```PageView``` event.

- Check [app/routes/view-content.tsx](./app/routes/view-content.tsx) to see how to send specific standard events using a Button to trigger / send the event.
- Check [app/routes/custom-content.tsx](./app/routes/cuctom-content.tsx) to see how to send your own custom events using a Button to trigger / send the event.


You should see warning in the browser console when you open your app in dev mode. That's fine.

Here's the result in Facebook Events Manager
![Screenshot 2024-11-04 at 21 27 43](https://github.com/user-attachments/assets/92127622-2575-4df6-8c2e-6479037b9cc7)


## Related Links

[Facebook Pixel Events Specification](https://developers.facebook.com/docs/meta-pixel/reference)
84 changes: 84 additions & 0 deletions facebook-pixel/app/root.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import {
Links,
Meta,
Outlet,
Scripts,
ScrollRestoration,
json,
useLoaderData,
useLocation,
} from "@remix-run/react";

// import the pixel client helper
import * as pixel from "./utils/pixel.client";
import { useEffect } from "react";

/**
* put the Your Pixel ID in the loader.
* Sure you can also put it in the .env
* @returns Lets
*/
export let loader = async () => {
return json({
PIXEL_ID: 'YOUR_PIXEL_ID_HERE',
});
};

export function Layout({ children }: { children: React.ReactNode }) {

const location = useLocation();

// get the pixel id
const { PIXEL_ID } = useLoaderData<typeof loader>();

// fire pixel init and the default pageview event here
// we use location as the depency index. so everytime the route changes, the pixel will fire
useEffect(() => {
pixel.init(PIXEL_ID);
pixel.pageView();
}, [location, PIXEL_ID]);

return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />

{/*
Facebook Pixel script injection. lets only activate it on Production only

You will also notice that I split script injection into 2 parts as seen below. After several atempts and trial, puting it as a single script tag as Facebook suggested did not work. It rise a hydation error in Production.

You should try it yourself and see it.
*/}
{process.env.NODE_ENV === "development" ? null : (
<>
<script
async
id="fb-pixel"
dangerouslySetInnerHTML={{
__html: `!(function(h,a,i,c,j,d,g){if(window.fbq){return}j=window.fbq=function(){j.callMethod?j.callMethod.apply(j,arguments):j.queue.push(arguments)};if(!window._fbq){window._fbq=j}j.push=j;j.loaded=!0;j.version="2.0";j.queue=[]})(window,document);`,
}}
/>
<script
async
src={`https://connect.facebook.net/en_US/fbevents.js`}
/>
</>
)}

<Meta />
<Links />
</head>
<body>
{children}
<ScrollRestoration />
<Scripts />
</body>
</html>
);
}

export default function App() {
return <Outlet />;
}
22 changes: 22 additions & 0 deletions facebook-pixel/app/routes/_index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Link } from "@remix-run/react";
export default function Index() {
return (
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.8" }}>
<h1>Welcome to Home Page</h1>
<br />
<p>
If you see this page, Pixel should fire <b>PageView</b> event by default
</p>
<p>
And now go to this route for firing another standard event <b>ViewContent</b>
<br />
<Link to="/view-content">ViewContent</Link>
</p>
<p>
And this route for firing a custom event
<br />
<Link to="/custom-event">CustomEvent</Link>
</p>
</div>
);
}
39 changes: 39 additions & 0 deletions facebook-pixel/app/routes/custom-event.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Link } from "@remix-run/react";

// import the pixel client helper
import * as pixel from "../utils/pixel.client";

export default function Index() {

const handleCustomtEvent = () => {
// define your own event name first
// in the facebook event manager dashboard
pixel.trackCustom("MyCustomEvent", {
content_name: "Contact Form",
content_category: "Form",
contents: [{
email: "some email",
phone: "some phone number",
}],
});

console.log("custom event fired");
}

return (
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.8" }}>
<h2>Example of firing Custom pixel event</h2>
<h5>Note that you need to define your own event name in the <a target="_blank" href="https://business.facebook.com/events_manager2">Facebook Event Manager</a></h5>

<button
type="button"
onClick={handleCustomtEvent}
>
Click Me to fire Custom event
</button>
<br />
<br />
<Link to="/">Go back</Link>
</div>
);
}
30 changes: 30 additions & 0 deletions facebook-pixel/app/routes/view-content.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Link } from "@remix-run/react";

// import the pixel client helper
import * as pixel from "../utils/pixel.client";


export default function Index() {

const handleFireEvent = () => {
// fire ViewContent event
pixel.track("ViewContent");

console.log("ViewContent event fired");
}

return (
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.8" }}>
<h2>Example of firing ViewContent pixel event</h2>
<button
type="button"
onClick={handleFireEvent}
>
Click Me to fire ViewContent event
</button>
<br />
<br />
<Link to="/">Go back</Link>
</div>
);
}
54 changes: 54 additions & 0 deletions facebook-pixel/app/utils/pixel.client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* Budi K
*/
declare global {
interface Window {
fbq: (
option: string,
event: string,
options?: Record<string, unknown>,
) => void
}
}

export const init = (pixelId: string) => {
if (!window.fbq) {
console.warn(
"window.fbq is not defined. This could mean your facebook pixel script has not loaded on the page yet.",
);
return;
}
console.log('initing pixel...');
window.fbq("init", pixelId);
}

export const pageView = () => {
if (!window.fbq) {
console.warn(
"window.fbq is not defined. This could mean your facebook pixel script has not loaded on the page yet.",
);
return;
}
console.log('firing pixel pageview...');
window.fbq("track", "PageView");
}

export const track = (event: string, options?: Record<string, unknown>) => {
if (!window.fbq) {
console.warn(
"window.fbq is not defined. This could mean your facebook pixel script has not loaded on the page yet.",
);
return;
}
window.fbq("track", event, options);
}

export const trackCustom = (event: string, options?: Record<string, unknown>) => {
if (!window.fbq) {
console.warn(
"window.fbq is not defined. This could mean your facebook pixel script has not loaded on the page yet.",
);
return;
}
window.fbq("trackCustom", event, options);
}
Binary file added facebook-pixel/image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
40 changes: 40 additions & 0 deletions facebook-pixel/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"name": "template",
"private": true,
"sideEffects": false,
"type": "module",
"scripts": {
"build": "remix vite:build",
"dev": "remix vite:dev",
"lint": "eslint --ignore-path .gitignore --cache --cache-location ./node_modules/.cache/eslint .",
"start": "remix-serve ./build/server/index.js",
"typecheck": "tsc"
},
"dependencies": {
"@remix-run/node": "^2.9.2",
"@remix-run/react": "^2.9.2",
"@remix-run/serve": "^2.9.2",
"isbot": "^4.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@remix-run/dev": "^2.9.2",
"@types/react": "^18.2.20",
"@types/react-dom": "^18.2.7",
"@typescript-eslint/eslint-plugin": "^6.7.4",
"@typescript-eslint/parser": "^6.7.4",
"eslint": "^8.38.0",
"eslint-import-resolver-typescript": "^3.6.1",
"eslint-plugin-import": "^2.28.1",
"eslint-plugin-jsx-a11y": "^6.7.1",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"typescript": "^5.1.6",
"vite": "^5.1.0",
"vite-tsconfig-paths": "^4.2.1"
},
"engines": {
"node": ">=20.0.0"
}
}
Binary file added facebook-pixel/public/favicon.ico
Binary file not shown.
7 changes: 7 additions & 0 deletions facebook-pixel/sandbox.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"hardReloadOnChange": true,
"template": "remix",
"container": {
"port": 3000
}
}
32 changes: 32 additions & 0 deletions facebook-pixel/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"include": [
"**/*.ts",
"**/*.tsx",
"**/.server/**/*.ts",
"**/.server/**/*.tsx",
"**/.client/**/*.ts",
"**/.client/**/*.tsx"
],
"compilerOptions": {
"lib": ["DOM", "DOM.Iterable", "ES2022"],
"types": ["@remix-run/node", "vite/client"],
"isolatedModules": true,
"esModuleInterop": true,
"jsx": "react-jsx",
"module": "ESNext",
"moduleResolution": "Bundler",
"resolveJsonModule": true,
"target": "ES2022",
"strict": true,
"allowJs": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"baseUrl": ".",
"paths": {
"~/*": ["./app/*"]
},

// Vite takes care of building everything, not tsc.
"noEmit": true
}
}
16 changes: 16 additions & 0 deletions facebook-pixel/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { vitePlugin as remix } from "@remix-run/dev";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";

export default defineConfig({
plugins: [
remix({
future: {
v3_fetcherPersist: true,
v3_relativeSplatPath: true,
v3_throwAbortReason: true,
},
}),
tsconfigPaths(),
],
});