jeffersongoncalves/laravel-fathom
A Laravel package for integrating Fathom Analytics into your application. Configure your Fathom site ID, inject the privacy-friendly tracking script, and start collecting lightweight, cookie-free analytics with minimal setup. Ideal for developers who want simple, privacy-focused website stats withou...
The package is a thin integration layer: it publishes a config file and registers a Blade-level way to emit Fathom's script.js tag. Treat it as a script-tag helper, not a full analytics SDK.
composer require jeffersongoncalves/laravel-fathom
fathom-config):
php artisan vendor:publish --tag="fathom-config"
.env:
FATHOM_SITE_ID=ABCDEFGH
config/fathom.php and confirm the key names, enabled flag, and any script URL/domain option. Because the package is small, reading the service provider and config file is faster than guessing at the README.<head>:
<x-fathom::script />
{{-- or, depending on the version: @fathom --}}
cdn.usefathom.com/script.js tag with your data-site value is present. That is the whole first use case: one layout change and you have pageview tracking.laravel-security-headers, etc.), view source again and confirm nonce="..." is on the tag. As of 4.1.0 this happens automatically — if the attribute is missing, your nonce is being generated after the layout renders or under a different key, not a problem with the package.Put it in the one layout everyone uses. Add the directive to resources/views/layouts/app.blade.php (or your Inertia root template) rather than to individual pages. Fathom tracks pageviews automatically from the script tag, so a single inclusion covers normal navigation.
Drive it from environment config. Keep separate site IDs for production and staging, and disable tracking outside production:
FATHOM_SITE_ID=ABCDEFGH
FATHOM_ENABLED=true
@if (config('fathom.enabled'))
<x-fathom::script />
@endif
This prevents local development, CI browser tests, and staging traffic from polluting production numbers.
Nonce-based CSP (4.1.0+). If you have moved off 'unsafe-inline', you no longer need to hand-roll the tag. Generate the nonce the Laravel way and the package will stamp it onto the script it renders:
// AppServiceProvider::boot()
Vite::useCspNonce();
// or, with a headers package
// middleware sets the CSP header + nonce, Laravel exposes it via csp_nonce()
<x-fathom::script /> {{-- renders <script src="..." nonce="{{ csp_nonce() }}"> --}}
Your header can then stay strict:
script-src 'self' 'nonce-{...}' https://cdn.usefathom.com;
connect-src 'self' https://api.usefathom.com;
The key ordering point: the nonce must exist before the layout renders. If you set it in a middleware, register that middleware globally or in the web group — a nonce generated in a controller or after the response is built will not reach the tag.
Guard against tracking internal pages. If you have admin panels or authenticated app routes, either skip the script there or gate it:
@guest
<x-fathom::script />
@endguest
Alternatively, wrap the package component in your own Blade component so the policy lives in one place.
Track conversions with Fathom events. The package only injects the script; goal tracking is plain Fathom JavaScript. Call it from your own small helper or Alpine component:
window.fathom?.trackEvent('signup_completed');
window.fathom?.trackEvent('checkout', { value: 49, currency: 'USD' });
Trigger these on form success, subscription changes, or checkout completion. Keep event names stable and low-cardinality so reports remain useful.
SPA and Inertia. Include the script once in the root HTML. For client-side route changes, call window.fathom?.trackPageview() from your router's afterEach hook (or Inertia's router.on('navigate')). Do not add the script again on each navigation.
Consent banners. If you need consent before analytics, do not render the component until consent is granted. Since it is just Blade, a session/cookie check around the include is enough — and the nonce still applies once you do render it, so no extra CSP work is needed on the deferred path.
Testing. In your test environment, set FATHOM_ENABLED=false and assert the tag is absent. For the enabled case, assert the rendered HTML contains your site ID rather than making network assertions. If you set a nonce in tests (e.g. Vite::useCspNonce('test-nonce')), you can assert the attribute too — useful for catching a regression where the nonce stops being propagated.
<script> tags. Your own inline snippets (the trackEvent calls, any bootstrap shim) are not covered — inline JS still needs a nonce/'unsafe-inline'/'unsafe-hashes of its own. Move such code into a nonce'd partial or an external file rather than assuming 4.1.0 solved inline CSP for you.Vite::useCspNonce() in a service provider or an early global middleware.FATHOM_SITE_ID or the enabled flag, run php artisan config:clear. A stale cached config is the most common reason the script "disappears".data-site, or omit it entirely depending on the version. Verify the rendered source after every deploy.script-src must still allow https://cdn.usefathom.com (a nonce does not whitelist an external origin by itself), and connect-src must allow Fathom's endpoint. 'strict-dynamic' in script-src is fine and is the usual companion to nonces, but it means host allowlists are ignored in CSP3 browsers — rely on the nonce, not the host list.script-src and connect-src.trackPageview() on every page load. Pick one model: automatic for server-rendered apps, manual for SPAs.trackPageview() call. Nonce'd scripts are re-evaluated on full navigation, so make sure the nonce is fresh per response, not cached with the layout HTML.php artisan view:clear if changes do not appear in production, especially with precompiled Blade views. This also applies after upgrading to 4.1.0 if the nonce attribute does not show up.How can I help you explore Laravel packages today?