Skip to content
LaravelBlog

livewire/livewire: Fix: root-level `Index` components no longer resolve to an empty name

LaravelBlogBot
LaravelBlogBot

Contributed by lazerg

Part of the livewire v4.4.7 release

Livewire v4 shipped a regression affecting a surprisingly common setup: a component class named Index sitting at the very root of your configured class_namespace — for example App\Livewire\Index. Instead of resolving to the name index, Livewire generated an empty string, and every attempt to render the component failed with a ComponentNotFoundException carrying the cryptic message Unable to find component: []. PR #10475 reorders the name-generation logic in the component finder so a root-level Index resolves to index again — the same name it had in v3.8.10. If you are upgrading from v3 and your app has an app/Livewire/Index.php file, this is the patch that keeps it working.

Categories: Fix (3), Upgrade notes (11)

What broke

Consider the classic "home page" component:

// app/Livewire/Index.php
namespace App\Livewire;

use Livewire\Component;

class Index extends Component
{
    public int $count = 1;

    public function increment()
    {
        $this->count++;
    }

    public function render()
    {
        return <<<'HTML'
            <div>Count: {{ $count }}</div>
        HTML;
    }
}

Under v3.8.10, this class resolved to the component name index, so all of the following worked:

<livewire:index />
Route::get('/', \App\Livewire\Index::class); // full-page component
Livewire::test(\App\Livewire\Index::class)->call('increment');

In early v4 releases, the same class produced an empty name. Rendering <livewire:index />, using the class as a route target, or even calling Livewire::new(Index::class)->getName() all failed — with the unhelpfully bracketed Unable to find component: [] error that made the underlying problem hard to spot.

Why it happened: .index was stripped too early

Internally, generateNameFromClass() converts a class name into a dotted candidate name and then strips the configured namespace. For App\Livewire\Index that produced app.livewire.index.

The bug was ordering. The method first removed a trailing .index — a convention where an Index component inside a subfolder takes the subfolder's name, so App\Livewire\Admin\Index becomes admin — and only then removed the namespace prefix. For a root-level Index, that collapsed app.livewire.index down to app.livewire before the namespace was stripped. Since the remainder now equaled the namespace, removing it left nothing at all: an empty component name.

The fix: strip the suffix after the namespace

The patched generateNameFromClass() now walks the registered class namespaces first, strips the matching prefix, and only afterwards checks for a trailing .index on whatever remains. Because the suffix check runs on the name after app.livewire has been removed, a root Index naturally falls back to index instead of collapsing to nothing.

The refactor also cleans up how named namespace prefixes are applied: the prefix (for example admin::) is captured during the namespace loop and concatenated after the suffix handling. That means an Index component registered under a named namespace resolves to admin::index rather than breaking the same way. In short:

Class Resolved name
App\Livewire\Index index
App\Livewire\Admin\Index admin
Root Index under named namespace admin admin::index

Upgrade impact from v3.8.10

For anyone coming from v3, this patch restores parity rather than introducing a migration. An Index component that worked in v3 works again, with the same name and no renames, config changes, or attributes required:

  • <livewire:index /> renders the root-level component.
  • Route::get('/...', Index::class) works as a full-page component.
  • Livewire::test(Index::class) works, including subsequent update requests like ->call('increment').

The subfolder convention is unchanged — App\Livewire\Admin\Index is still addressed as admin. If you hit the v4 bug and worked around it, for example by explicitly registering the class with Livewire::component('index', Index::class) or by renaming the class entirely, those workarounds are now optional: remove them or leave them, both are fine.

The PR ships with regression coverage at two levels. Unit tests on the finder assert that a root Index resolves to index — and to admin::index when registered under a named namespace. An integration suite mirrors an app with app/Livewire/Index.php by setting livewire.class_namespace before boot, then verifies getName() returns index, page-component routing renders, <livewire:index /> renders by name, and a subsequent update request round-trips correctly.

Takeaways

  • Root-level Index components (e.g. App\Livewire\Index) resolve to index again, matching v3.8.10 behavior.
  • The .index-to-subfolder convention is untouched: App\Livewire\Admin\Index is still admin.
  • Named namespaces handle root Index correctly too (admin::index).
  • No code or config changes are needed on upgrade — just make sure your v4 install includes this fix.
  • Optional cleanup: drop any workarounds like manual Livewire::component() registrations for these components.

Sources

More from this release

Related Articles

Fix 5 mins read

livewire/livewire: Recover from failed server responses by reverting rejected updates

Before this patch, a single failed request could permanently wedge a Livewire component. If a server-side error — a stray `dd()`, an `abort(500)`, an exception in an `updated` hook — rejected a property update, dismissing the error modal wasn't the end of it: every subsequent interaction replayed the rejected update, failed with the same error, and re-opened the modal. The only escape was a full page refresh. This release fixes that by reverting server-rejected updates on the client so the component recovers gracefully, and it cleans up the unhandled promise rejections those failures produced. There are no public API changes, so if you're on v3.8.10 this is a drop-in bugfix upgrade.

LaravelBlogBot
LaravelBlogBot