still-code/laravel-umami
Laravel Umami is a lightweight integration for the privacy-friendly Umami analytics platform. Easily add analytics to your Laravel application, track page views and custom events with Blade directives, middleware, and simple configuration—no cookies or complex setup required.
Requires: Laravel 10/11/12, PHP 8.1+. Version 5.0.3 is the first line with explicit Laravel 12 support (added in PR #39); the rest of the release is Dependabot housekeeping, so there are no breaking changes to plan around.
Minimal setup (about 5 minutes):
composer require still-code/laravel-umami
php artisan vendor:publish --tag="umami-config"
Set the two values you actually need in .env:
UMAMI_ENABLED=true
UMAMI_HOST=https://analytics.example.com
UMAMI_WEBSITE_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Drop the tracking snippet into your layout's <head> (typically resources/views/layouts/app.blade.php):
<x-umami::script />
Where to look first: the published config/umami.php. It is the whole package in one file — enable/disable switch, self-hosted host, website ID. If something is not behaving, that file is the first and usually last place you need to check. After config, the README's Blade snippet section is the second.
First use case: pageview tracking on a marketing site or app. Add the component to the layout, deploy, and confirm in the Umami dashboard that pageviews appear within a minute or two. Do this in staging first with a separate Umami website entry so you don't pollute production stats.
Layout-level pageview tracking. The default and by far the most common pattern: one component in the base layout, and every full page load is tracked automatically. Do not sprinkle the component across partials or individual views — that leads to duplicate beacons on pages that include multiple layouts.
Consent gating. Because the component is just a Blade render, wrap it in your consent check:
@if (Cookie::get('analytics_consent') === 'granted')
<x-umami::script />
@endif
This keeps the GDPR/ePrivacy story simple and auditable: no script tag in the HTML at all until consent is recorded. The trade-off is that consent must be resolved server-side; if you use a client-side consent banner, gate on the client instead (render the component but have your banner block it) or accept a reload after consent.
Server-side events for the things the browser misses. Pageviews are only half the story. For signups, form submissions, checkout completions, and other actions worth measuring, fire an event from the controller or an event listener:
use StillCode\LaravelUmami\Facades\Umami;
Umami::track('signup', ['plan' => $user->plan]);
Do this in a queued listener when the request path is latency-sensitive — the package makes an outbound HTTP call to your Umami instance, and you do not want that inline on a checkout response.
SPAs, Livewire, and Inertia. The script is injected once per full document load. Client-side route changes will not produce additional pageviews unless you call the Umami browser API yourself (umami.track(...)) from your router's navigation hook. For Livewire, wire it into Livewire.hook('morph.updated') or a navigate callback. Treat the Blade component as "load the library" and everything else as manual instrumentation.
Per-tenant or multi-site setups. If you host several brands from one codebase, override the config at runtime before the layout renders — for example in a tenancy middleware:
config([
'umami.website_id' => $tenant->umami_website_id,
'umami.host' => $tenant->umami_host,
]);
Because the component reads config at render time, this works without touching the package. Keep the shared .env values as a fallback so local development does not blow up when no tenant is bound.
Environment separation. Keep UMAMI_ENABLED=false in .env.testing and .env.local. There is no reason for a test suite or a developer machine to hit your analytics host, and it removes a whole class of flaky outbound-HTTP failures from CI. If you need to assert tracking in a feature test, bind a fake or use Http::fake([...]) and assert against the recorded request rather than the real endpoint.
UMAMI_WEBSITE_ID is the website UUID, not the domain. This is the single most common misconfiguration. Copy the ID from Umami's Settings → Websites panel. A wrong ID means successful HTTP 200s with no data.UMAMI_HOST. Use https://analytics.example.com, not https://analytics.example.com/. A double slash in the script URL resolves on most servers but breaks on some reverse proxies and CDNs..env values run php artisan config:cache on deploy and php artisan config:clear locally. If the script disappears in staging but not on your machine, this is almost always why.laravel-csp or a hand-rolled header), you need the Umami host in both script-src and connect-src. Forgetting connect-src produces a silent failure: the script loads, the beacon is blocked, and nothing appears in the dashboard.UMAMI_ENABLED=false stops the Blade component, but if you call the facade directly in a controller it will still make the HTTP call unless the package honours the flag there too — verify this for your version, and consider wrapping your own calls in if (config('umami.enabled')).try/catch or push them onto a queue with retry. An analytics outage taking down your signup flow is an avoidable incident.composer.json constraint allows it and that you are not pinning an older transitive dependency. Run the suite after upgrading, but do not expect behaviour changes.How can I help you explore Laravel packages today?