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

Mobile Detect Bundle Laravel Package

netbull/mobile-detect-bundle

Symfony bundle integrating Mobile Detect to identify phones, tablets, and desktop browsers. Provides services and Twig helpers for device-aware templates, redirects, and responsive behavior in Laravel-style PHP apps.

View on GitHub
Deep Wiki
Context7

Getting Started

First, a reality check on scope. netbull/mobile-detect-bundle is a Symfony bundle, not a Laravel package. It cannot be composer required into a Laravel app (it depends on symfony/framework-bundle, DI, Twig, and a kernel request listener). What you actually get day-to-day splits into two paths:

  • You're in a Symfony app (or a Symfony service inside your domain) → install the bundle.
  • You're in Laravel → install the underlying mobiledetect/mobiledetectlib and borrow the bundle's three ideas: a detector service, a view-switch parameter, and a redirect listener.

Symfony path

composer require netbull/mobile-detect-bundle

Check the resolved version before you plan anything. The project is on a 7.x line and shipped v7.0.2 in May 2025, so it is actively maintained and the major version is roughly aligned with the modern Symfony generation. Don't trust a "Symfony 2/3/4/5 only" line you may have read in older docs — inspect the constraints directly:

composer show netbull/mobile-detect-bundle --all | grep -E 'versions|requires'

Register the bundle (namespace is MobileDetectBundle\):

// config/bundles.php
return [
    // ...
    MobileDetectBundle\MobileDetectBundle::class => ['all' => true],
];

Then look at the config tree first — the whole package is config-driven, so config/packages/mobile_detect.yaml is your real documentation:

mobile_detect:
    devices:
        mobile: ['/iphone/i', '/android.*mobile/i']
        tablet: ['/ipad/i']
    switch_device_view:
        device_view_enabled: true      # enables the ?device_view= / cookie override
        default_view: desktop
        devices:
            mobile: mobile
            tablet: tablet
    redirect:
        mobile:
            host: m.example.com
            action: redirect
            status_code: 302           # 302 while rolling out, 301 only when settled
        tablet:
            host: tablet.example.com
        detect_tablet_as_mobile: false

Verify what actually loaded rather than trusting the README:

bin/console debug:config mobile_detect
bin/console debug:twig | grep -i -E 'mobile|tablet|device'

First use case to implement: the "View desktop site" escape hatch. Wire the switch parameter into your layout, because it's the single feature that saves you from a support ticket storm when UA detection guesses wrong:

{% if is_mobile() and not device_view_is_full() %}
  <a href="?device_view=desktop">View desktop site</a>
{% endif %}

Laravel equivalent (the practical route)

composer require mobiledetect/mobiledetectlib
// app/Http/Middleware/RedirectMobile.php
public function handle($request, Closure $next)
{
    $detect = new \Detection\MobileDetect();   // v3 namespace; v2 is Mobile_Detect

    $excluded = $request->is('api/*', 'webhooks/*', 'assets/*', 'livewire/*');

    if (! $excluded
        && $detect->isMobile()
        && ! $detect->isTablet()
        && $request->getHost() !== 'm.example.com'
        && $request->cookie('device_view') !== 'desktop'
    ) {
        return redirect()->away('https://m.example.com'.$request->getRequestUri(), 302);
    }

    return $next($request);
}

Implementation Patterns

1. Inject the detector, not a facade of the detector. The bundle exposes MobileDetectorInterface and a DeviceView helper. Autowire them into controllers/services rather than calling globals — this is what makes your code testable:

use MobileDetectBundle\DeviceDetector\MobileDetectorInterface;
use MobileDetectBundle\Helper\DeviceView;

final class HomeController extends AbstractController
{
    public function __construct(
        private MobileDetectorInterface $detector,
        private DeviceView $deviceView,
    ) {}

    public function __invoke(): Response
    {
        $view = $this->deviceView->getViewType();          // desktop|tablet|mobile
        $template = match ($view) {
            'mobile' => 'home/mobile.html.twig',
            'tablet' => 'home/tablet.html.twig',
            default  => 'home/desktop.html.twig',
        };

        return $this->render($template, ['detector' => $this->detector]);
    }
}

2. Template-per-device over one responsive template — only when you must. The bundle's switch_device_view is designed for the legacy pattern of separate mobile/tablet/desktop templates. Use it for email, PDF, or a genuinely different mobile UX; don't use it to reinvent CSS media queries.

3. Keep detection behind your own interface. Wrap the bundle (or the raw library in Laravel) in App\Device\DeviceResolver with isMobile(), isTablet(), preferredView(). When UA sniffing inevitably gets replaced by Client Hints (Sec-CH-UA-Mobile) or a responsive-only approach, you swap one class instead of grepping the codebase.

4. Redirect listener hygiene. Redirects run on every request. In practice you want a subscriber that short-circuits before the bundle's listener for paths that must never redirect: /api, /webhooks, /health, static assets, and any route with an _no_mobile_redirect default. In Laravel, that's the $excluded check above; in Symfony it's a higher-priority kernel.request listener.

5. Test with an explicit UA table, not live detection.

$client = static::createClient();
$client->setServerParameter('HTTP_USER_AGENT',
    'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15');
$client->request('GET', '/');
self::assertResponseRedirects('https://m.example.com/');

Also unit-test DeviceResolver with a fixed array of [ua => expectedView] so a library upgrade can't silently change routing.

6. Twig-level branching for partials is cheap and fine: {% if is_mobile() %} around a heavy carousel or an autoplay video. Keep it to component-level decisions; never use it for layout.

7. Pin the patch, then upgrade deliberately. v7.0.2 is a bug-fix-only release (a deprecation fix), so it's a safe in-range bump — but pin an exact version in composer.json and read every changelog entry for the 7.x line before bumping the minor, because this project's release notes are terse (* deprecation) and tell you nothing about behavioural impact.

Gotchas and Tips

  • Not a Laravel package. Don't add it to a Laravel composer.json — take mobiledetect/mobiledetectlib and the three design ideas (detector service, view-switch parameter, redirect listener).
  • Don't trust stale Symfony-version claims — check constraints. The 7.x line with a 2025 patch release points at modern Symfony, contradicting older "Symfony 2/3/4/5 only" notes. Run composer why-not netbull/mobile-detect-bundle and composer show --all before designing around it; if composer blocks you, fall back to the Laravel/library route rather than forking blindly.
  • Deprecation noise is the main thing v7.0.2 buys you. The release is literally "fix deprecation." If you're on PHP 8.3/8.4 or Symfony 6.4/7.x and pinned to 7.0.1, you were probably logging deprecation notices from bundle code on every request. Bumping to 7.0.2 clears them — worth doing, zero-risk, but it is not a feature release and changes nothing about UA behaviour.
  • Small-maintainer risk still applies. Sparse release notes, a single-maintainer cadence, and a narrow niche mean you are effectively the QA team. Pin an exact version and keep your wrapper (App\Device\DeviceResolver) thin enough that you could vendor the handful of classes you actually use (detector wrapper + listener + DeviceView) into your own namespace.
  • UA detection is structurally unreliable. iPadOS 13+ reports a macOS Safari UA, so isTablet() returns false on modern iPads. ChromeOS tablets and Android tablets are similarly messy. Treat isTablet() as a heuristic and default to the responsive view.
  • 301 redirects get browser-cached forever. During rollout use status_code: 302. Users who get a bad 301 to m.example.com cannot be rescued by a config change.
  • Cache poisoning. If your view/redirect depends on the device_view cookie or the UA, your CDN/Varnish must Vary: User-Agent (and ideally Vary: Sec-CH-UA-Mobile, Sec-CH-UA-Platform). Otherwise the first mobile visitor's redirect gets served to desktop users. This is the most common production incident with bundles like this.
  • Config nesting is the #1 install bug. detect_tablet_as_mobile lives under redirect, not at the root; devices appears both at root (regexes) and under switch_device_view (template names). Always confirm with bin/console debug:config mobile_detect after editing.
  • Twig function names are version-dependent. is_mobile(), is_tablet(), and the device_view_* helpers vary between releases; debug:twig is faster than reading source. If a template throws "unknown function," you're on a version whose extension didn't register.
  • Autowiring may fail on older versions. If MobileDetectorInterface isn't aliased, inject the concrete service or define an alias in services.yaml. Locate the real id with bin/console debug:container | grep -i mobile.
  • Debugging tip: to prove the bundle is the culprit, disable it (device_view_enabled: false, remove the redirect block) and retest. Symptom "only curl/API requests get redirected" is almost always the global listener, not your code.
  • Extension points worth knowing: custom regexes under devices; decorating DeviceView to add a "remember my choice" cookie; and a kernel.request subscriber with higher priority than the bundle's listener to skip redirects for specific routes. In Laravel the equivalents are a route-group-scoped middleware and a View::composer or Blade directive.
  • Long-term: for a new project, prefer CSS/container-query responsive design plus Sec-CH-UA-Mobile server hints. Use this bundle (or the library) only for the redirect/escape-hatch layer and for emails, where UA sniffing is still the only signal you get.
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.
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
equipc/laravel-email-database-log
adedsec/nova-translated-labels
wazza/laravel-db-encryption
hokny/laravel-generator
vonsogt/backup
jengo/queues
jeremykenedy/laravel-observability
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums