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

Browser Detect Laravel Package

pataar/browser-detect

Detect browsers, operating systems, devices, and bots from the user agent in your Laravel application. pataar/browser-detect provides a simple API to identify visitors and tailor responses, logging, or feature checks based on their platform.

View on GitHub
Deep Wiki
Context7

Getting Started

pataar/browser-detect is a thin Laravel wrapper around matomo/device-detector. You get a Browser facade that parses the current request's User-Agent and answers questions about it.

composer require pataar/browser-detect

Laravel package discovery registers Pataar\BrowserDetect\BrowserDetectServiceProvider and the Browser facade alias automatically. Publish the config so you can tune caching:

php artisan vendor:publish --tag=config
# or: --provider="Pataar\BrowserDetect\BrowserDetectServiceProvider"

First use case — a diagnostic route to see what the detector reports for a real request:

use Browser;

Route::get('/whoami', fn () => [
    'bot'       => Browser::isBot(),
    'mobile'    => Browser::isMobile(),
    'tablet'    => Browser::isTablet(),
    'desktop'   => Browser::isDesktop(),
    'browser'   => Browser::browserName(),     // "Chrome"
    'version'   => Browser::browserVersion(),  // "126.0.6478.127"
    'platform'  => Browser::platformName(),    // "iOS"
    'device'    => Browser::deviceType(),      // "smartphone"
    'family'    => Browser::deviceFamily(),    // "iPhone"
]);

Where to look first:

  1. config/browser-detect.php — the only knob you'll touch (cache settings).
  2. Pataar\BrowserDetect\Agent — the object behind the facade; every Browser::x() call is forwarded to it. Read this class before assuming a method exists.
  3. Matomo\DeviceDetector\DeviceDetector upstream docs — the wrapper only exposes a subset; anything missing can be reached via Browser::getDeviceDetector()-style access or by parsing manually.

Upgrading to 5.4.0: non-breaking. composer update pataar/browser-detect and, if you had published the config, diff it against the vendor copy — 5.4.0 introduces a new device-detector cache option that won't appear in your existing config file. Then php artisan config:clear (or config:cache again if you cache config in production).

Implementation Patterns

Presentation-only branching (the common case). Use it for "show the app-store banner on mobile" rather than for behaviour that matters:

Route::get('/', function () {
    return view(Browser::isMobile() ? 'home.mobile' : 'home.desktop');
});

Middleware for device-aware redirects. Keep it early in the stack so downstream code doesn't re-parse:

class RedirectMobileUsers
{
    public function handle(Request $request, Closure $next)
    {
        if (! Browser::isBot() && Browser::isMobile() && ! $request->is('m/*')) {
            return redirect('/m' . $request->getPathInfo());
        }

        return $next($request);
    }
}

Blade helpers. Wrap the facade once so views stay readable and you have a single place to add caching or test doubles:

@if (Browser::isMobile())
    <a href="{{ $iosUrl }}">Get the app</a>
@endif

Parsing a UA that isn't the current request. Browser::parse($ua) returns a fresh Agent for an arbitrary string — use this in imports, webhook handlers, and queue jobs:

$agent = Browser::parse($log->user_agent);

if ($agent->isBot()) {
    return;
}

Testing. Don't stub the facade for simple cases — set the header and let the real detector run. It's deterministic:

$this->withHeader('User-Agent', 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) ...')
    ->get('/')
    ->assertSee('Get the app');

For unit tests where you only care about the branch, Browser::shouldReceive('isMobile')->andReturn(true) works, but prefer Browser::parse() with a fixture UA so you also exercise your real-world UA strings.

Caching (the 5.4.0 headline). Device detection is regex-heavy — every request without a cache re-runs dozens of patterns. There are two layers worth understanding:

  • The package-level Agent cache (existing cache.lifetime style setting) memoises a parsed result per UA.
  • 5.4.0 adds a device-detector cache option, which plugs Laravel's cache into the detector's own internal lookups (regex/version resolution), so repeated parses across many different UAs get faster, not just repeated parses of the same UA.

Turn both on for any endpoint that sees real traffic:

// config/browser-detect.php — check the published file for exact keys after 5.4.0
'cache' => [
    'lifetime' => 3600,       // seconds; 0 disables the package-level cache
    // new in 5.4.0: detector-internal cache wiring
    // (store / prefix / ttl — names depend on the release, verify in the config)
],

Point the cache at Redis or another shared store if you run multiple app servers; a per-node array store gives you almost nothing.

Gotchas and Tips

  • Check isBot() first. Every other predicate is meaningless for crawlers, and isDesktop()/isMobile() will happily return a value for Googlebot.
  • A User-Agent is attacker-controlled. Never use this package for authorisation, rate limiting, or "mobile users get a cheaper plan" logic. It's for rendering decisions only.
  • CLI and queue workers have no request UA. Inside a queued job, Browser::isMobile() describes whatever the worker's environment reports — nothing useful. Always use Browser::parse($storedUserAgent) there.
  • Long-lived workers (Octane, Swoole, queue daemons) can leak state. The facade memoises the resolved agent on first access. If you use Browser::setUserAgent() anywhere, reset it per request, or just prefer Browser::parse() which returns a fresh instance and can't bleed between requests.
  • config:cache hides new keys. After upgrading to 5.4.0, a config-cached deployment will silently ignore the new cache option until you re-run php artisan config:cache. Verify with php artisan tinker → config('browser-detect').
  • Lifetime 0 means "off", not "forever". It's a common misread when copying configs between projects.
  • Cache keys must be version-aware. If you upgrade matomo/device-detector (or the wrapper) and the cache still holds results parsed by the old regex database, you can serve stale/incorrect device data for the cache TTL. Flush the tags/prefix you configured for this package on deploy.
  • Bot detection is not exhaustive. isBot() covers the known-bot list; custom scrapers, headless Chrome with a spoofed UA, and new AI crawlers routinely slip through. Log the UAs you see in production and revisit periodically.
  • Detection data ages. Brand-new browser versions and devices are unknown until you bump matomo/device-detector, which is why pataar/browser-detect releases follow upstream. Keep it on a scheduled update rather than pinning for a year.
  • Extension point is upstream, not the wrapper. If you need a method the Agent class doesn't expose, drop to the underlying DeviceDetector instance rather than patching the package — your fork will be the thing you forget to update.
  • Debug surprises with the raw string. When a result looks wrong, dump $request->userAgent() — proxies and CDNs occasionally rewrite or strip the header, and you'll be debugging the detector for something that never arrived.
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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin