label84/laravel-tagmanager
A lightweight Laravel package for Google Tag Manager integration. Configure your GTM container, inject the required scripts, and push data layer events from PHP or Blade with a clean, simple API - no manual snippet handling.
composer require label84/laravel-tagmanager.php artisan vendor:publish --provider="Label84\TagManager\TagManagerServiceProvider" --tag=configAppServiceProvider::boot() (or a dedicated TagServiceProvider if you prefer):
use Label84\TagManager\Facades\TagManager;
public function boot(): void
{
TagManager::add('google-analytics', config('services.google.analytics_id'));
TagManager::add('google-tag-manager', config('services.google.tag_manager_id'));
}
<head>
{!! TagManager::head() !!}
</head>
<body>
{!! TagManager::body() !!}
</body>
<head> and the noscript/body portion renders after <body>.config/services.php or config/tagmanager.php and pull them from .env. This avoids hardcoding UA-…/GTM-… IDs in service providers and lets you swap per environment (e.g. no analytics in local and testing).TagManager::add() in an environment check:
if (app()->isProduction()) {
TagManager::add('google-analytics', $id);
}
StartSession so session/consent state is available before the layout renders.merge() method to inject additional configuration (e.g. anonymize_ip, consent mode values) into an already-registered tag rather than duplicating add() calls:
TagManager::add('google-analytics', $id);
TagManager::merge('google-analytics', ['anonymize_ip' => true]);
Since v1.5.3, merge() correctly accepts arrays, so you can pass nested config structures without wrapping workarounds.head() and body() aggregate every registered tag. Keep the render calls in a single base layout rather than scattering them across partials to avoid double-loading scripts.assertSee('GTM-', false)) or temporarily disable registration in your TestCase::setUp() to keep snapshots stable. If the package ships a fake/disable switch in your version, prefer that over asserting on raw snippet output.{!! TagManager::head() !!} / {!! TagManager::body() !!}. Using {{ }} will HTML-encode the scripts and silently break tracking.add() calls in boot() run on every request; if you also call add() in a controller or middleware for the same key you can end up with duplicate or unexpected entries. Pick one place per tag.merge() behavior (v1.5.3). Prior to 1.5.3, merge() mishandled array payloads. If you’re pinned to ≤1.5.2 and passing arrays, the merge silently produced incorrect config — upgrade to v1.5.3 or wrap arrays explicitly. This is a patch release with no API changes, so upgrading is low risk.config/tagmanager.php, run php artisan config:clear (or config:cache in deploy). Stale cached config is a common cause of “my tag isn’t showing up” in production.<head> as practical; GTM/GA expect the container snippet before other scripts for reliable pageview timing.script-src allowances for googletagmanager.com / google-analytics.com and img-src for the GA pixel — otherwise tracking fails silently in the browser console.TagManager state isn't directly supported, so log the resolved ID inside the provider when narrowing issues.How can I help you explore Laravel packages today?