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

Laravel Mobile Detect Laravel Package

riverskies/laravel-mobile-detect

Laravel wrapper around the Mobile Detect library that identifies phones, tablets, and desktops from the user agent. Provides a clean facade, Blade directives, and configurable detection so you can serve device-specific views and content with minimal boilerplate.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and confirm it loads

composer require riverskies/laravel-mobile-detect

On Laravel 5.5+ package discovery registers Riverskies\Laravel\MobileDetect\MobileDetectServiceProvider automatically. On older apps add the provider and the MobileDetect facade alias to config/app.php manually. There is nothing to publish or migrate — detection is driven entirely by the request's User-Agent through mobiledetect/mobiledetectlib.

Where to look first

  1. vendor/riverskies/laravel-mobile-detect/src/MobileDetectServiceProvider.php — this is the whole package. It registers the Blade directives (@mobile, @desktop, @tablet plus their @else* / @end* counterparts) and the container binding behind the facade. Read it before assuming a directive name exists.
  2. vendor/mobiledetect/mobiledetectlib/MobileDetect.php — the real detection API (isMobile(), isTablet(), isIOS(), version('iOS'), is('Chrome')). Everything the package does is a thin wrapper over this.
  3. The README for the canonical directive example.

First use case: a device-specific include in your layout

The 10-minute win is swapping a heavy desktop nav or hero for a lighter mobile variant, without duplicating the whole layout:

{{-- resources/views/layouts/app.blade.php --}}
@mobile
    @include('partials.nav-mobile')
@elsedesktop
    @include('partials.nav-desktop')
@endmobile

Or, for a single conditional block:

@desktop
    <div class="mega-menu">...</div>
@enddesktop

Clear compiled views once after installing (php artisan view:clear) so the new directives are picked up.

Implementation Patterns

Decide in PHP, not in Blade, when logic grows

Blade directives are convenient for markup branching but get unwieldy for anything with more than two branches. Push the decision into a controller, middleware, or view composer and pass a simple value down:

use Riverskies\Laravel\MobileDetect\Facades\MobileDetect;

class HomeController extends Controller
{
    public function index()
    {
        return view('home', [
            'device'   => MobileDetect::isTablet() ? 'tablet' : (MobileDetect::isMobile() ? 'mobile' : 'desktop'),
            'liteMode' => MobileDetect::isMobile(),
        ]);
    }
}

Then @if($liteMode) in the view. This keeps the view testable and makes it trivial to stub the value.

Share device class app-wide via middleware

For a project that touches device type in many views, resolve it once per request and share it:

public function handle($request, Closure $next)
{
    $detect = app('mobile-detect'); // check the provider for the exact binding key

    View::share('device', match (true) {
        $detect->isTablet() => 'tablet',
        $detect->isMobile() => 'mobile',
        default             => 'desktop',
    });

    return $next($request);
}

Caveat: resolve through the container once and reuse, rather than calling the facade in a loop of 50 product cards.

Surface the device to JavaScript and CSS

Server-side detection complements, not replaces, responsive CSS. A common pattern is to stamp the device class on <body> or <html> so client-side behaviour (lazy-load thresholds, slider config) can key off it:

<body class="@mobile is-mobile @endmobile @desktop is-desktop @enddesktop">

Or emit a meta tag / data attribute rather than branching large chunks of markup.

Feature gating rather than layout forking

The pattern that ages best: serve identical markup and use the detector to gate optional features — autoplay video, an interactive map, a heavy third-party widget, infinite scroll:

@desktop
    @include('partials.map-interactive')
@elsedesktop
    <img src="{{ $staticMapUrl }}" alt="Location">
@endmobile

If you find yourself maintaining two entirely different page trees, stop and use responsive CSS instead.

Redirects and subdomains

In middleware, before the response is generated:

if (MobileDetect::isMobile() && ! $request->is('m/*') && ! $request->isMethod('post')) {
    return redirect()->to('https://m.example.com' . $request->getRequestUri());
}

Guard against POST requests and preserve query strings, otherwise form submissions die.

Testing

Set the user agent explicitly on the request and assert on the rendered output:

$response = $this->withHeader('User-Agent', 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15')
                 ->get('/');

$response->assertSee('nav-mobile');
$response->assertDontSee('mega-menu');

If the header override appears to be ignored, the underlying detector has cached or read $_SERVER['HTTP_USER_AGENT'] directly — set that in the test as a fallback and reset it in tearDown(). For controller-level tests, bind a fake detector into the container rather than simulating a UA.

Gotchas and Tips

It is user-agent sniffing, and user agents lie

MobileDetect parses a string the client controls. Treat the result as a presentation hint, never as trust or authorization. Edge cases that bite in production:

  • iPadOS 13+ reports a desktop Safari UA — isTablet() returns false for iPads set to "Request Desktop Website". Modern Android tablets are also inconsistent.
  • Googlebot uses a smartphone UA for mobile-first indexing, so the HTML Google indexes may be your "mobile" branch. Keep content parity across branches or you will lose rankings / be flagged for cloaking.
  • bots, scrapers, and spoofers land in whatever bucket their UA claims.
  • Desktop-mode toggles in mobile browsers flip the result.

Caching will betray you

If you branch HTML on device and then put it behind a CDN, a page cache, or Cache::remember, desktop users will get the mobile markup or vice versa. Either vary the cache key by device class, send Vary: User-Agent, or (preferably) keep device-specific markup out of cached pages and do it client-side. This is the single most common way this package causes a "random broken layout" bug.

Blade directive nesting and @else

The package's branches compile to raw if/else PHP. Do not mix a bare @else inside a @mobile block and expect it to bind to an outer @if — use the package's own @elsemobile/@elsedesktop/@endmobile pairs, and verify the exact set of directive names in the service provider for your installed version. Also, after upgrading the package, run php artisan view:clear; stale compiled views can keep referencing directives that no longer exist.

No HTTP request, no meaningful answer

In console commands, queue workers, and some tests there is no request UA — the detector falls back to the PHP process's $_SERVER, which typically yields "desktop". Never let a queued job's behaviour depend on isMobile(); pass the device decision into the job as data.

Package age is the real constraint

Last release was 2017, so the Composer constraints (illuminate/support 5.x) will block installation on Laravel 9/10/11. Options, in order of preference:

  1. Skip it — mobiledetect/mobiledetectlib is still maintained; bind it in a tiny service provider and register your own @mobile directives with Blade::if('mobile', fn () => app(MobileDetect::class)->isMobile()). Laravel's Blade::if gives you real @mobile ... @elsemobile ... @endmobile semantics for free.
  2. Fork the package and widen the version constraints — the source is genuinely ~50 lines, so this is low-risk.
  3. Use jenssegers/agent for a richer, maintained wrapper over the same library.

Performance and extension points

UA parsing is a regex pass over a short string — cheap, but resolve the detector once per request (container singleton or a single facade call) rather than inside loops over collections. To reach capabilities the package doesn't expose, resolve the underlying MobileDetect instance and call it directly — isIOS(), version('Android'), is('Chrome') — or decorate the binding in your own service provider with a class that adds methods like isOldAndroid(); the container binding is the extension point, so you can swap it wholesale in tests.

Don't build a separate site on this

The maintenance cost of two full view trees is high and the SEO risk is real. Use detection for progressive enhancement, redirects, and light feature gating; use CSS media queries for layout.

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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource