liberusoftware/analytics-meta-livewire
A Laravel Livewire package from Liberu Software for managing analytics metadata in your application, providing ready-made components to configure and display meta tracking information with minimal setup.
Understand what this is first. liberusoftware/analytics-meta-livewire is a presentation adapter — it wires Livewire components to Meta's server-side Conversions API (CAPI) through a companion core package. It almost certainly does not implement Graph API calls itself; expect a dependency such as liberusoftware/analytics-meta-core (or equivalent) to own the HTTP client, token handling, and event payload building. Your first job is to figure out which package does what.
Read in this order:
composer.json — it reveals the core package, required Livewire version (^2 vs ^3 matters a lot), and Laravel constraints.src/*ServiceProvider.php — look for register(), boot(), Livewire::component(...), Blade::component(...), config merges, and container bindings. This is the map of the whole package.config/*.php — pixel ID, access token, test event code, enabled environments, queue settings.src/Livewire/ (or src/Http/Livewire/) and any src/Concerns/ / src/Traits/ — the actual developer-facing surface is usually a trait or a set of components.tests/ — with 0 stars and no real docs, the test suite is the authoritative usage example. If there are no tests, treat the package as experimental.Minimal setup:
composer require liberusoftware/analytics-meta-livewire
# check the provider for the real tag names before running
php artisan vendor:publish --provider="Liberu\Analytics\Meta\Livewire\ServiceProvider"
META_PIXEL_ID=1234567890
META_CAPI_ACCESS_TOKEN=EAAG... # system user token, not the app secret
META_TEST_EVENT_CODE=TEST12345 # staging only
Then php artisan config:clear and confirm the provider is auto-discovered (or add it to bootstrap/providers.php / config/app.php depending on Laravel version).
First use case: a lead form. Add the tracking trait to one Livewire component and dispatch a single Lead event from the success branch of your submit action — not from mount(), not from updated(). Give it a stable event_id so the browser pixel and the server event deduplicate:
use Liberu\Analytics\Meta\Livewire\Concerns\TracksMetaEvents; // verify exact FQCN in src/
class LeadForm extends Component
{
use TracksMetaEvents;
public string $eventId;
public string $email = '';
public function mount(): void
{
$this->eventId = (string) Str::uuid();
}
public function submit(): void
{
$this->validate();
Lead::create(['email' => $this->email]);
$this->trackMeta('Lead', [
'event_id' => $this->eventId,
'event_source_url' => url()->current(),
'custom_data' => ['content_name' => 'newsletter'],
]);
}
}
<script>
fbq('track', 'Lead', { content_name: 'newsletter' }, { eventID: '{{ $eventId }}' });
</script>
If the trait name or method signature doesn't exist, the component-based API (<livewire:analytics-meta::... /> or a dispatchMetaEvent() helper) is the other likely shape — grep src/ for function track and function dispatch to find it.
Trait-per-component, dispatch-on-success. The dominant pattern for this kind of adapter is a trait that resolves the current request context (URL, user agent, IP, _fbp/_fbc cookies, session ID) and hands it to the core package. Attach it only to components that actually produce events — don't put it on a base class that every component inherits, or you'll pay the resolution cost on every render.
Deduplication via shared event_id. This is the single most important pattern. Generate the ID once in mount(), store it as a public property so it survives re-renders, render it into the browser pixel call, and pass the same value to the server event. Meta merges them by event_id + event_name. Without this you will see inflated counts in Events Manager and never be able to reconcile the two sources.
Centralize payload shaping. Rather than hand-building custom_data in every component, create a small MetaEventPayload builder or value object in your app and have components call $this->trackMeta('Purchase', $payload->forOrder($order)). Currency, value, content_ids, and content_type must match the browser event exactly or Meta will flag the pair as mismatched.
Consent and environment gating. Wrap dispatch in your own consent service rather than trusting the package:
if (! app(ConsentService::class)->allowsMarketing()) {
return; // don't dispatch, don't render the browser snippet
}
Also confirm the package respects app()->environment() for test_event_code — if it doesn't, gate it yourself, or you'll pollute production data from local development.
Queue where possible. Livewire actions are synchronous XHRs. A blocking Graph API call inside submit() adds latency directly to the user's perceived response time. If the core package exposes a queued job (look for ShouldQueue in its src/Jobs/), prefer it; otherwise wrap the call in dispatchAfterResponse() so the HTTP round-trip happens after the response is flushed. Ensure a worker is running.
Multi-tenant / multi-pixel. If you're building per-tenant apps, don't rely on a single config value. Bind the pixel ID into the container per request and check whether the adapter resolves it from the container or reads config() directly — the latter will break you.
Testing pattern. Set META_TEST_EVENT_CODE in the testing env and Http::fake() the Graph endpoint to assert the outgoing JSON body. Assert event_name, event_id, and that user_data fields are hashed. Test the negative case too: a failed validation must not enqueue an event.
Blade drop-in for page views. If the package ships a Blade component for PageView, use it in your layout rather than hand-rolling — it likely handles _fbp/_fbc cookie capture consistently. But note Livewire's wire:navigate will not re-fire it; register a livewire:navigated listener if you're on Livewire 3 SPA mode.
The re-render duplicate problem. The classic failure mode: a tracking call placed in mount(), updated(), or render() fires on every property change, Livewire poll, and wire:model.live keystroke. Keep a private bool $dispatched = false guard on the component and only dispatch from explicit user actions.
Never track in updated() when using wire:model.live. A search box will emit an event per character. If you need "user engaged with form" events, debounce or use a one-shot flag.
event_source_url and action_source. CAPI rejects or misattributes events without action_source: 'website'. If the adapter doesn't default it, set it explicitly — and remember that for Livewire requests url()->current() is the component's page URL, which may not be the URL the user actually saw if you're using wire:navigate. Pass the intended URL down from the Blade view if it matters.
PII must be normalized and hashed. Email lowercase + trim, phone in E.164 without + or punctuation, then SHA-256. Verify whether the package does this or expects raw values — getting it wrong either leaks PII into logs or produces user_data quality scores of 3/10 in Events Manager.
Server-side is invisible to ad blockers, but the browser pixel isn't. That's the entire point of CAPI. But if you only send server-side and skip the browser pixel, you lose automatic matching signals (fbp, fbc, automatic advanced matching). Best results come from sending both with matched event_ids.
Test events are sticky. test_event_code in a cached production config will black-hole all your real traffic into the Test Events tab. Add a deploy check or gate it on app()->environment('local', 'staging') in your own config layer.
Errors are almost certainly swallowed. Adapters in this space tend to try/catch and log at debug, which means a wrong token or revoked permission fails silently for weeks. Temporarily raise the log level for the analytics namespace, or add an Event::listen on whatever failure event the core package dispatches, and alert on it. Analytics must never break checkout — but it must not fail invisibly either.
Config caching bites. php artisan config:cache in CI with a config/analytics-meta.php that reads env() at runtime is fine, but if the package's config file has already been merged, publishing it again won't overwrite. Delete the published file before re-publishing.
Livewire version coupling. This is the sharpest edge. Livewire 2 and 3 differ in component registration, mount signatures, and lifecycle hooks. Given the package's near-zero adoption, assume it targets exactly one version. Check composer.json before you commit.
License is NOASSERTION. That is not a permissive license — it means the repository's LICENSE file is missing or unparseable. Get legal sign-off, or at minimum read the LICENSE/COPYING file directly, before shipping this in anything commercial.
Extension points, in rough order of usefulness: publishable config, container bindings in the service provider (swap the core client for a fake in tests), any dispatched events around send/failure, and the src/Jobs/ classes if you need to override queue/connection behavior. If none of those exist, your only real extension point is decorating the core package's client binding yourself.
Debugging checklist when events don't appear: (1) Is META_ACCESS_TOKEN a system user token with ads_management on the right ad account? (2) Is the pixel ID the one attached to that ad account? (3) Check the Events Manager Test Events tab with test_event_code set — it gives per-event error messages the API hides behind a 200. (4) Log the raw payload before it's sent; nine times out of ten the bug is a malformed custom_data or a duplicate event_id.
How can I help you explore Laravel packages today?