Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Filament Gtag Laravel Package

jeffersongoncalves/filament-gtag

A Filament plugin that adds Google Analytics gtag.js tracking to your admin panel, with configurable measurement IDs and simple setup for monitoring panel usage and visitor activity.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install all layers. The plugin is a Filament UI on top of jeffersongoncalves/laravel-gtag, spatie/laravel-settings, and (as of 3.4.0) filament-analytics-core ^3.1:

    composer require jeffersongoncalves/filament-gtag
    

    Pulling the plugin should drag in the rest; verify with composer show jeffersongoncalves/filament-gtag and check that filament-analytics-core resolves to >=3.1 — the new navigationGroup() API depends on it.

  2. Publish and migrate. spatie/laravel-settings stores values in the database, so a migration is required before the UI can save anything:

    php artisan vendor:publish --tag=filament-gtag-config
    php artisan vendor:publish --tag=laravel-gtag-config   # if you need script-level overrides
    php artisan migrate
    

    If the plugin ships its own settings migration tag (commonly filament-gtag-settings or settings-migrations), publish that instead — check vendor/jeffersongoncalves/filament-gtag/database/settings first.

  3. Register the plugin on the panel you want the settings page to appear in:

    use JeffersonGoncalves\FilamentGtag\FilamentGtagPlugin;
    
    public function panel(Panel $panel): Panel
    {
        return $panel
            ->plugins([
                FilamentGtagPlugin::make()
                    ->navigationGroup('Integrations'),   // new in 3.4.0
            ]);
    }
    

    Omit navigationGroup() and the page keeps its translated default group, exactly as before.

  4. Where to look first.

    • config/filament-gtag.php — navigation group, page slug, access gate.
    • src/Settings/GtagSettings.php (or src/GtagSettings.php) — the actual settings class. This is the source of truth for property names (enabled, tracking_id, measurement_id, etc.).
    • src/Filament/Pages/… — the form schema; it mirrors the settings properties.
    • src/FilamentGtagPlugin.php — the plugin's public fluent API (navigationGroup(), and whatever else has accreted since 1.x). Read this before subclassing anything.
    • resources/lang/ — 17 locales as of 1.3.0.
  5. First use case: open the new "Google Analytics" page in the panel, toggle it on, paste a GA4 measurement ID (G-XXXXXXXXXX), save, then load a front-end page and search the HTML source for googletagmanager.com/gtag/js. If the script is absent, the problem is almost always in step 2 (migration) or the front-end layout not using the hook that laravel-gtag injects into.

Implementation Patterns

Read settings in application code, not the DB directly. Always resolve the typed settings object — it is cached and respects spatie's serialization:

use JeffersonGoncalves\FilamentGtag\Settings\GtagSettings;

$settings = app(GtagSettings::class);

if ($settings->enabled && $settings->tracking_id) {
    // e.g. pass to a Blade view, a consent banner, or a queued event
}

Gate the plugin per panel. In a multi-panel app you usually only want the settings page on an admin panel, and often only in production:

->plugins([
    ...(app()->isProduction() ? [FilamentGtagPlugin::make()] : []),
])

File the page into your own nav group (3.4.0). Instead of subclassing the page just to override getNavigationGroup(), pass the group at registration. The argument is string|Closure, so the group can be computed per-request — useful for role-dependent menus or a group name that comes from a translation key:

FilamentGtagPlugin::make()
    ->navigationGroup(fn () => __('admin.groups.marketing'))

Keep the string identical to the group you already use for your other marketing pages; Filament matches groups by label, and a typo silently creates a second group rather than erroring. If your panel groups are declared as enums or constants, use those — don't hand-roll the string in two places.

Seed defaults for reproducibility. Because settings live in the DB, an empty settings table row on a fresh deploy means the plugin's page renders with nulls. Add defaults in a seeder rather than relying on the form:

app(GtagSettings::class)->enabled = app()->isProduction();
app(GtagSettings::class)->tracking_id = config('services.gtag.id');
app(GtagSettings::class)->save();

This is also the cleanest way to keep secrets out of the panel — keep the DB value blank and inject the ID at deploy time.

Programmatic writes for support workflows. A common pattern: an artisan command or an admin action that flips analytics off during an incident without a deploy:

$settings = app(GtagSettings::class);
$settings->enabled = false;
$settings->save();   // clears the settings cache for you

Pair with the underlying package's config. laravel-gtag typically owns where the script is injected (a Blade directive or a view hook in your layout). The Filament plugin owns what the ID is. Keep the script tag in resources/views/layouts/app.blade.php and let the plugin supply the values — don't duplicate the gtag.js snippet by hand.

Testing. Use RefreshDatabase and run the settings migrations in TestCase. In tests, set settings explicitly instead of hitting the Filament page:

app(GtagSettings::class)->fill(['enabled' => true, 'tracking_id' => 'G-TEST123'])->save();
$this->get('/')->assertSee('G-TEST123', false);

If you assert on navigation placement, assert the rendered group label rather than introspecting the plugin object — that covers the Closure form too.

Local/staging hygiene. Give non-production panels a separate settings scope or simply leave the plugin unregistered and the enabled flag false. Nothing is worse than staging traffic polluting a production GA4 property.

Gotchas and Tips

  • Settings cache is the #1 source of "my change didn't apply." Spatie caches per settings class. save() invalidates it, but direct DB edits (or a restore from a dump) do not. Run php artisan settings:clear-cache — and remember to run it in your deploy script if settings are ever written outside the app.
  • navigationGroup() requires filament-analytics-core >= 3.1. If you've pinned the core package lower (or an old composer.lock kept it back), the method either doesn't exist or is ignored and the page silently stays in the translated default group. If the group doesn't change after upgrading the plugin, check composer show filament-analytics-core before touching your panel code.
  • Adding a field means three edits. To expose a new option (e.g. anonymize_ip) you must touch the settings class, its migration (spatie settings migrations are additive per property group), and the Filament form schema. Missing the migration yields a silent null or a MissingSettingsMigration-style error.
  • Validate the ID format in the form, not downstream. GA4 IDs are G-…; legacy Universal Analytics UA-… IDs are dead. If the plugin's form doesn't enforce a pattern, add ->regex('/^G-[A-Z0-9]+$/') to the field or you will ship a broken tag that fails silently in the browser (gtag logs a console warning but nothing else).
  • CSP and nonces. If you run a strict Content-Security-Policy, googletagmanager.com must be allowlisted and any inline gtag bootstrap needs a nonce. The plugin does not solve this; expect a blank analytics dashboard until you add the directives.
  • Consent mode is out of scope. This plugin stores an ID and toggles the tag — it is not a consent management platform. For GDPR regions, wrap the call in Google Consent Mode v2 (gtag('consent', 'default', {...})) before the gtag script loads, and only set analytics_storage: 'granted' after your banner accepts. Do not treat enabled = true as consent.
  • Ad blockers and server-side rendering. Roughly a third of visitors block googletagmanager.com; if you're rendering via SSR/Inertia the script may load before the DOM is ready. Check the network tab, not just the page source.
  • Duplicate tags on multi-panel or multi-domain setups. If the plugin's page is registered on several panels and your layouts share a component, you can end up with two gtag.js includes. Verify by counting occurrences of gtag/js?id= in the rendered HTML.
  • Don't use the panel page for secrets. Tracking IDs are public by definition (they appear in every page's source), but if you later extend the settings class with an API secret for Measurement Protocol, keep it in .env — the settings table is readable by anyone with DB access and visible in the Filament form.
  • Version coupling. 3.4.0 requires filament-analytics-core ^3.1; earlier 1.x lines required laravel-gtag ^3.0. If you're upgrading across majors, expect config key renames in the underlying packages and possibly a moved class name for the settings object — diff config/laravel-gtag.php and grep src/ for the settings namespace after every major bump rather than assuming your published copy still matches.
  • Translations shipped, but your labels may not be. The 17 locales cover the plugin's own UI strings — including the default navigation group label, which is why navigationGroup() is the only way to get consistent casing with the rest of your menu. Any custom fields you add to the form need their own ->label(__('...')) entries.
  • Debugging checklist, in order: (1) is the plugin registered on this panel? (2) does a row exist in the settings table? (3) is enabled true? (4) does the layout actually render the laravel-gtag hook? (5) does the browser console show a CSP or 404 error? Nine times out of ten it's step 2 or 4.
  • Extension point: navigationGroup() removes the single most common reason to subclass the page. Subclass only when you need to change the slug, the access gate, or the form schema — then point the plugin at your class and keep the settings object untouched.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator