livewire/livewire: Fix: root-level `Index` components no longer resolve to an empty name
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
Indexcomponents (e.g.App\Livewire\Index) resolve toindexagain, matching v3.8.10 behavior. - The
.index-to-subfolder convention is untouched:App\Livewire\Admin\Indexis stilladmin. - Named namespaces handle root
Indexcorrectly 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.