Learn how to implement Meta Pixel (Facebook Pixel) on your Craft CMS website using Twig templates, plugins, and environment-aware configuration.
Prerequisites
- Active Meta Business Manager account
- Meta Pixel ID (format:
123456789012345) - Craft CMS 4.x or 5.x installation
- Basic understanding of Twig templating
Method 1: Direct Twig Template Integration
Step 1: Configure Environment Variables
Add your Meta Pixel ID to .env:
# .env
META_PIXEL_ID="123456789012345"
ENVIRONMENT="production"
Step 2: Create Meta Pixel Partial Template
Create a reusable partial at templates/_analytics/meta-pixel.twig:
{# templates/_analytics/meta-pixel.twig #}
{% set pixelId = getenv('META_PIXEL_ID') %}
{% set environment = craft.app.config.general.environment %}
{# Only load Meta Pixel in production and not during Live Preview #}
{% if pixelId and environment == 'production' and not craft.app.request.isLivePreview %}
<!-- Meta Pixel Code -->
<script>
!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 }}');
fbq('track', 'PageView');
</script>
<noscript>
<img height="1" width="1" style="display:none"
src="https://www.facebook.com/tr?id={{ pixelId }}&ev=PageView&noscript=1"/>
</noscript>
<!-- End Meta Pixel Code -->
{% elseif craft.app.config.general.devMode %}
<!-- Meta Pixel disabled in dev mode -->
{% endif %}
Step 3: Include in Base Layout
Add Meta Pixel to your main layout template:
{# templates/_layouts/base.twig #}
<!DOCTYPE html>
<html lang="{{ currentSite.language }}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ title ?? siteName }}</title>
{# Meta Pixel - Load in <head> #}
{{ include('_analytics/meta-pixel') }}
{# Other head content #}
{{ head() }}
</head>
<body>
{{ beginBody() }}
{# Main content #}
{% block content %}{% endblock %}
{{ endBody() }}
</body>
</html>
Method 2: Using SEOmatic Plugin
Step 1: Install SEOmatic
cd /path/to/craft-project
composer require nystudio107/craft-seomatic
Step 2: Configure in Control Panel
- Navigate to SEOmatic → Tracking Scripts
- Click Facebook Pixel
- Enter your Pixel ID in the Facebook Pixel ID field
- Configure environment settings:
- Environment: Set to
productiononly - Send Page View: Enabled by default
- Environment: Set to
Step 3: Environment Configuration
Create or update config/seomatic.php:
<?php
use craft\helpers\App;
return [
'*' => [
'pluginName' => 'SEOmatic',
'renderEnabled' => true,
'environment' => App::env('ENVIRONMENT') ?: 'production',
],
'production' => [
'renderEnabled' => true,
'facebookPixel' => [
'id' => App::env('META_PIXEL_ID'),
'sendPageView' => true,
],
],
'staging' => [
'renderEnabled' => false,
],
'dev' => [
'renderEnabled' => false,
],
];
Method 3: Custom Module Implementation
For advanced control and server-side events:
Step 1: Create Meta Pixel Module
mkdir -p modules/metapixel
Create modules/metapixel/Module.php:
<?php
namespace modules\metapixel;
use Craft;
use craft\events\TemplateEvent;
use craft\web\View;
use yii\base\Event;
use yii\base\Module as BaseModule;
class Module extends BaseModule
{
public static $instance;
public function init()
{
parent::init();
self::$instance = $this;
// Inject Meta Pixel script
Event::on(
View::class,
View::EVENT_END_HEAD,
[$this, 'injectMetaPixel']
);
}
public function injectMetaPixel(TemplateEvent $event)
{
// Only inject in production
if (Craft::$app->config->general->environment !== 'production') {
return;
}
// Skip during Live Preview
if (Craft::$app->request->isLivePreview) {
return;
}
$pixelId = getenv('META_PIXEL_ID');
if (!$pixelId) {
return;
}
$script = $this->renderMetaPixelScript($pixelId);
$event->output = str_replace('</head>', $script . '</head>', $event->output);
}
private function renderMetaPixelScript(string $pixelId): string
{
return <<<HTML
<!-- Meta Pixel Code -->
<script>
!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}');
fbq('track', 'PageView');
</script>
<noscript>
<img height="1" width="1" style="display:none"
src="https://www.facebook.com/tr?id={$pixelId}&ev=PageView&noscript=1"/>
</noscript>
<!-- End Meta Pixel Code -->
HTML;
}
}
Step 2: Bootstrap the Module
Edit config/app.php:
<?php
use craft\helpers\App;
return [
'modules' => [
'metapixel' => [
'class' => \modules\metapixel\Module::class,
],
],
'bootstrap' => ['metapixel'],
];
Multi-Site Configuration
For Craft multi-site installations with different pixels:
{# templates/_analytics/meta-pixel.twig #}
{% set pixelIds = {
'siteHandleOne': getenv('META_PIXEL_SITE_ONE'),
'siteHandleTwo': getenv('META_PIXEL_SITE_TWO'),
'siteHandleThree': getenv('META_PIXEL_SITE_THREE'),
} %}
{% set pixelId = pixelIds[currentSite.handle] ?? null %}
{% if pixelId and craft.app.config.general.environment == 'production' %}
<!-- Meta Pixel Code -->
<script>
!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 }}');
fbq('track', 'PageView');
</script>
<noscript>
<img height="1" width="1" style="display:none"
src="https://www.facebook.com/tr?id={{ pixelId }}&ev=PageView&noscript=1"/>
</noscript>
<!-- End Meta Pixel Code -->
{% endif %}
Environment variables:
# .env
META_PIXEL_SITE_ONE="111111111111111"
META_PIXEL_SITE_TWO="222222222222222"
META_PIXEL_SITE_THREE="333333333333333"
Advanced Event Matching
Track user data for improved attribution (GDPR-compliant):
{# templates/_analytics/meta-pixel.twig - With Advanced Matching #}
{% set pixelId = getenv('META_PIXEL_ID') %}
{% set currentUser = currentUser ?? null %}
{% if pixelId and craft.app.config.general.environment == 'production' %}
<script>
!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');
{% if currentUser %}
// Advanced matching with hashed user data
fbq('init', '{{ pixelId }}', {
em: '{{ currentUser.email|hash('sha256') }}',
fn: '{{ currentUser.firstName|hash('sha256') }}',
ln: '{{ currentUser.lastName|hash('sha256') }}'
});
{% else %}
fbq('init', '{{ pixelId }}');
{% endif %}
fbq('track', 'PageView');
</script>
{% endif %}
Content Security Policy (CSP)
Configure CSP headers to allow Meta Pixel:
// config/general.php
return [
'*' => [
'securityHeaders' => [
'Content-Security-Policy' => implode('; ', [
"default-src 'self'",
"script-src 'self' 'unsafe-inline' https://connect.facebook.net",
"img-src 'self' data: https://www.facebook.com",
"connect-src 'self' https://www.facebook.com",
]),
],
],
];
Cookie Consent Integration
Implement GDPR-compliant loading based on consent:
{# templates/_analytics/meta-pixel.twig - With Cookie Consent #}
{% set cookieConsent = craft.cookies.get('cookie_consent') %}
{% set pixelId = getenv('META_PIXEL_ID') %}
{% if cookieConsent == 'all' or cookieConsent == 'marketing' %}
{% if pixelId and craft.app.config.general.environment == 'production' %}
<!-- Meta Pixel Code -->
<script>
!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 }}');
fbq('track', 'PageView');
</script>
{% endif %}
{% else %}
{# Show consent banner #}
{{ include('_components/cookie-consent-banner') }}
{% endif %}
Performance Optimization
DNS Prefetch and Preconnect
Add resource hints to improve loading performance:
{# In <head> section #}
<link rel="dns-prefetch" href="//connect.facebook.net">
<link rel="preconnect" href="https://connect.facebook.net" crossorigin>
<link rel="preconnect" href="https://www.facebook.com" crossorigin>
Lazy Loading
Delay Meta Pixel loading for non-critical pages:
<script>
// Load Meta Pixel after page load
window.addEventListener('load', function() {
!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 }}');
fbq('track', 'PageView');
});
</script>
Testing and Validation
Verify Pixel Installation
- Browser Console: Check for
fbqfunction - Network Tab: Verify requests to
facebook.com - Meta Pixel Helper: Install Meta Pixel Helper Chrome extension
- Events Manager: Check Test Events in Meta Events Manager
Debug Mode in Development
Enable debug output for local testing:
{% if craft.app.config.general.devMode %}
<script>
// Mock fbq for development
window.fbq = window.fbq || function() {
console.log('Meta Pixel Event:', arguments);
};
// Initialize mock pixel
fbq('init', '{{ pixelId ?? 'DEV_MODE' }}');
fbq('track', 'PageView');
console.log('Meta Pixel running in debug mode');
</script>
{% endif %}
Test Events Tool
Use Meta's Test Events feature:
{% set testEventCode = craft.app.request.getParam('test_event_code') %}
{% if testEventCode %}
<script>
fbq('init', '{{ pixelId }}', {}, {
testEventCode: '{{ testEventCode }}'
});
</script>
{% endif %}
Access with: https://yoursite.com?test_event_code=TEST12345
Common Issues and Solutions
Pixel Not Loading
Add debug output:
{% if craft.app.config.general.devMode %}
<!-- Meta Pixel Debug Info -->
{% set pixelId = getenv('META_PIXEL_ID') %}
<!-- Pixel ID: {{ pixelId ? pixelId : 'NOT SET' }} -->
<!-- Environment: {{ craft.app.config.general.environment }} -->
<!-- Live Preview: {{ craft.app.request.isLivePreview ? 'Yes' : 'No' }} -->
{% endif %}
AdBlockers Blocking Pixel
AdBlockers commonly block Meta Pixel. Solutions:
- Use Conversions API (server-side tracking)
- Inform users about content personalization
- Test with AdBlocker disabled
CSP Header Blocking
Ensure CSP headers allow Facebook domains:
'script-src' => "'self' 'unsafe-inline' https://connect.facebook.net",
'img-src' => "'self' data: https://www.facebook.com",
'connect-src' => "'self' https://www.facebook.com",
User Privacy Considerations
Limited Data Use
For California residents (CCPA compliance):
<script>
fbq('init', '{{ pixelId }}');
fbq('dataProcessingOptions', ['LDU'], 1, 1000); // California
fbq('track', 'PageView');
</script>
Disable Automatic Configuration
For better privacy control:
<script>
fbq('init', '{{ pixelId }}', {}, {
autoConfig: false,
debug: {{ craft.app.config.general.devMode ? 'true' : 'false' }}
});
fbq('track', 'PageView');
</script>
User Role Exclusion
Exclude admin users from tracking:
{% set currentUser = currentUser ?? null %}
{% set shouldLoadPixel = not currentUser or not currentUser.isInGroup('admins') %}
{% if shouldLoadPixel %}
{# Load Meta Pixel #}
{% endif %}
GraphQL API Integration
For headless Craft CMS, expose pixel configuration:
query {
globalSet(handle: "siteSettings") {
... on siteSettings_GlobalSet {
metaPixelId
enableMetaPixel
}
}
}
Implement in frontend:
// Frontend JavaScript
function loadMetaPixel(pixelId) {
!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);
fbq('track', 'PageView');
}
Next Steps
- Event Tracking - Track custom events and conversions
- E-commerce Tracking - Track Craft Commerce events
- GTM Integration - Deploy Meta Pixel via GTM