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

Active Laravel Package

hieu-le/active

Laravel helper for detecting the active state of routes URLs and requests, making it easy to highlight the current menu item, tab, or link in your navigation. Provides simple isActive methods and Blade-friendly checks with flexible pattern matching.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package with Composer:

composer require hieu-le/active

On Laravel 5.5+ the service provider and the Active facade are registered by package auto-discovery, so there is no config to publish and no service provider to add manually.

Where to look first

  1. The GitHub README (it is archived, but the README is still the only documentation).
  2. src/HieuLe/Active/ActiveClass.php — the whole package is a single, small class. Reading it end-to-end takes two minutes and tells you exactly how matching works.
  3. composer.json of the package to confirm which Laravel versions it was tested against before you add it to a modern app.

First use case — a nav link

Once installed, a handful of global helper functions are available in Blade. The typical pattern is: the helper returns a boolean, and active_class() turns that boolean into a class string.

{{-- routes/web.php: Route::get('/', HomeController::class)->name('home'); --}}
<li class="{{ active_class(if_route('home')) }}">
    <a href="{{ route('home') }}">Home</a>
</li>

The available helpers are:

Helper Purpose
if_route('users.index') Current route name equals the given name
if_route_pattern('admin.*') Current route name matches a * wildcard pattern
if_action('App\Http\Controllers\UserController@index') Current request maps to a controller action
if_url('/about') Current URL matches
active_class($condition, $active = 'active', $inactive = '') Converts a truthy condition into a class string

If you only ever need named routes, the equivalent native Laravel call is request()->routeIs('home'), which you should keep in the back of your mind as the drop-in replacement (see Gotchas).

Implementation Patterns

1. A single nav-link partial / Blade component

Rather than repeating active_class(if_route(...)) everywhere, wrap it once:

{{-- resources/views/components/nav-link.blade.php --}}
@props(['route', 'active' => 'active', 'inactive' => ''])

@php($isActive = if_route_pattern($route))

<a href="{{ route(str_replace('*', '', $route) ?: $route) }}"
   class="{{ active_class($isActive, $active, $inactive) }} {{ $attributes->get('class') }}">
    {{ $slot }}
</a>
<x-nav-link route="dashboard">Dashboard</x-nav-link>
<x-nav-link route="admin.users.*">Users</x-nav-link>

This is the pattern most teams converge on: the package stays an implementation detail of one component.

2. Section highlighting with wildcards

if_route_pattern() is where the package earns its keep over plain routeIs in older Laravel versions. Prefix every route in a group and match the group:

Route::prefix('admin')->name('admin.')->group(function () {
    Route::get('/users', [UserController::class, 'index'])->name('users.index');
    Route::get('/roles', [RoleController::class, 'index'])->name('roles.index');
});
<li class="{{ active_class(if_route_pattern('admin.*')) }}">Administration</li>
<li class="{{ active_class(if_route_pattern('admin.users.*')) }}">Users</li>

Because admin.users.* matches admin.users.index, admin.users.create, admin.users.edit, the parent link stays highlighted for detail pages — the thing every sidebar needs and hand-rolled code usually gets wrong.

3. Combining conditions for aliased pages

When two routes should light up the same link, combine them with plain PHP — the helpers return booleans:

<li class="{{ active_class(if_route('profile.show') || if_route('profile.edit')) }}">Profile</li>

Same for pattern + exact: if_route('dashboard') || if_route_pattern('reports.*').

4. Controller-action matching for legacy apps

If you inherited an app where routes are not named, use if_action() to key off the controller:

<li class="{{ active_class(if_action('App\Http\Controllers\Billing\InvoiceController@index')) }}">Invoices</li>

Prefer route names where you can — action strings are long and break on refactors, whereas route names are stable.

5. Facade for non-Blade contexts

The Active facade is useful in view composers, menu builders, or a NavigationService:

use HieuLe\Active\Facades\Active;

class MenuBuilder
{
    public function items(): array
    {
        return [
            ['label' => 'Users', 'url' => route('users.index'),
             'active' => Active::isActiveRoutePattern('users.*')],
        ];
    }
}

This keeps the "what is active" decision in PHP and lets the Blade partial stay dumb.

6. Custom active/inactive classes

Bootstrap, Tailwind, and Bulma all want different class strings. Pass them explicitly instead of hardcoding active:

{{-- Tailwind --}}
<a class="{{ active_class(if_route('home'), 'text-white font-bold', 'text-gray-400') }}">Home</a>

{{-- Bulma --}}
<li class="{{ active_class(if_route('home'), 'is-active') }}">Home</li>

7. Multi-level menus

For a menu that should highlight both the top-level section and the deepest item:

<ul>
@foreach ($sections as $section)
    <li class="{{ active_class(if_route_pattern($section['prefix'].'.*'), 'open', '') }}">
        <a class="{{ active_class(if_route_pattern($section['prefix'].'.*')) }}">{{ $section['label'] }}</a>
        <ul>
            @foreach ($section['children'] as $child)
                <li class="{{ active_class(if_route($child['route'])) }}">...</li>
            @endforeach
        </ul>
    </li>
@endforeach
</ul>

8. Testing it

Nothing special is required — assert against the rendered markup:

public function test_users_link_is_active(): void
{
    $this->get(route('users.index'))
         ->assertSee('class="active"', false);
}

Gotchas and Tips

The package is archived — plan the exit

Last release March 2022, no maintainer, 408 stars, zero dependents. It has not been run against Laravel 10/11/12. The entire feature set is one class of roughly a hundred lines. For any new project, the native replacement is one line:

{{-- replaces if_route_pattern('admin.*') --}}
<li class="{{ request()->routeIs('admin.*') ? 'active' : '' }}">

request()->routeIs() has supported * wildcards for years, and request()->routeIs() / request()->is() cover the if_url case. If you are inheriting a codebase that uses the package heavily, the cheapest long-term fix is to add these two helpers to app/Support/helpers.php and shadow the package's API rather than rewriting every Blade file:

if (! function_exists('active_class')) {
    function active_class(bool $condition, string $active = 'active', string $inactive = ''): string
    {
        return $condition ? $active : $inactive;
    }
}

Then drop the Composer dependency and leave the templates untouched.

Removed Laravel helpers

Older releases relied on str_is(), which was deprecated in Laravel 5.8 and removed thereafter. If you pin an early version on a modern Laravel you will get a Call to undefined function str_is() at the first wildcard match — and only at the first wildcard match, so it can survive smoke tests. Use the latest release, or avoid wildcards.

Unnamed routes return null

if_route() compares against request()->route()->getName(). On 404 pages, redirects, closure routes defined without ->name(), or console/queue contexts there is no route at all, and request()->route() is null. In a layout rendered from an error page this throws. Guard it:

{{ active_class(request()->route() && if_route('home')) }}

The Blade-safe habit: always wrap the helper in active_class() (which coerces the value) rather than using if_route() directly in an @if.

if_url('/') is a trap

The root URL matches as a prefix of everything in most implementations, so a "Home" link built with if_url('/') stays permanently active. Always use if_route('home') for the homepage, and reserve if_url() for routes you genuinely cannot name.

Query strings and trailing slashes

if_url() compares against the request path/URL — ?page=2, a trailing slash, or a locale prefix (/en/about) will all cause a miss. If your app is localized with a prefix, strip it before comparing, or (better) name your routes and use if_route().

Inertia, Livewire, and SPAs

This is a server-side, full-page-render helper. With Inertia or an SPA router, client-side navigation does not re-render the layout from the server, so the active class will be stale after the first click. In those stacks the active state must be computed in JavaScript from the current URL — the package has nothing to offer there. With Livewire, navigation does go through the server, but only the component re-renders; if your nav is outside the component it will not update. Use wire:navigate with a full page visit, or compute active state inside the Livewire component.

active_class() takes a boolean, not a route name

The most common mistake is active_class('home') — a non-empty string is truthy, so everything is always active. The correct composition is always active_class(if_route('home')). If you want a one-argument form, write your own wrapper:

function is_active_route(string $route): string
{
    return active_class(if_route_pattern($route));
}

Debugging an "always active" link

The package is stateless and reads only the current request, so the bug is almost always in the route name. Dump the actual value first:

{{-- temporary --}}
<!-- route: {{ optional(request()->route())->getName() ?? 'NONE' }} -->

Nine times out of ten the answer is NONE (unnamed route), a group prefix you forgot (admin.users.index vs users.index), or a wildcard pattern that is broader than intended — if_route_pattern('user*') matches users.index and user-settings.index.

No config, no extension points

There is no published config file and no container binding you are meant to override. The class is concrete and not designed for swapping. If you need different matching semantics (e.g. ignoring a locale segment, or treating edit/show as the same menu item), you cannot extend it cleanly — define your own helper that delegates to request()->routeIs(), which gives you full control and removes the dependency.

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.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib