Skip to content

[4.x] Fix component named Index resolving to an empty name - #10475

Merged
joshhanley merged 2 commits into
livewire:4.xfrom
lazerg:fix/root-index-component-name
Aug 3, 2026
Merged

[4.x] Fix component named Index resolving to an empty name#10475
joshhanley merged 2 commits into
livewire:4.xfrom
lazerg:fix/root-index-component-name

Conversation

@lazerg

@lazerg lazerg commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

A component class named Index at the root of the configured class_namespace (for example App\Livewire\Index) throws ComponentNotFoundException with an empty name ("Unable to find component: []").

generateNameFromClass() strips a trailing .index before it removes the namespace prefix, so app.livewire.index collapses to app.livewire. That makes the name equal to the namespace, so removing the prefix leaves an empty string. A root-level Index now falls back to index, the name it resolved to under v3.

Fixes #10473

Comment thread src/Finder/Finder.php Outdated
// An index component at the namespace root collapses to an empty name, so restore 'index'...
if ($name === '') {
$name = 'index';
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

feels weird to me to not solve this problem further upstream.

like won't there be a case now where a truly empty string name (which admittedly would be weird) gets upgraded to index? maybe that's even desired behavior i suppose?

does feel kind of funny to use "Index" as a root level name anyways but I see that this is the most logical behavior: look for a a class called "Index"


So this handles the case of Index at the root of the class namespace - what about the case of looking for index.blade.php as the root of the SFC...

Also is this in the codepath of namespaced components and what effect would that have?

Just something to think a little harder about.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for reworking this. Stripping the namespace before the .index reads much cleaner than my empty-string guard, and I agree it's better to never produce the empty name than to restore it after the fact.

On the SFC question you raised: single and multi-file components never go through generateNameFromClass. Their name comes from the tag (<livewire:index /> gives index), and resolveSingleFileComponentPath / resolveMultiFileComponentPath turn that name into a path. A root-level index.blade.php resolves through the plain singleFile lookup at {location}/index.blade.php, and an index/ directory through multiFileAsIndex, so nothing on the view side ever collapses to an empty name. The class path was the only one deriving a name backwards from a fully qualified string, which is why it was the only place hitting this.

And for namespaced components it's the same loop, so an Index at a registered namespace root now comes back as admin::index instead of admin::, which the namespace test you added covers.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, that matches what I found — thanks for confirming the SFC side, your read is right. Nothing derives a name from a view path anywhere in src/, so the class path really was the only backwards direction.

Two things worth recording here:

The bug is nastier than the issue reports. The empty name flows into generateClassFromName(), which builds \App\Livewire\\Index — note the double backslash. Composer PSR-4-resolves that to the same file under a spelling PHP doesn't consider already-loaded, so it requires it a second time and you get a hard Fatal error: Cannot redeclare class, not just the ComponentNotFoundException. Depends on load order which one you hit.

The realistic trigger is a page component. A root-level Index is almost always somebody's index page, i.e. Route::get('/', Index::class) — which is exactly the class→name direction. Referencing it by name (<livewire:index />) was never broken, which is probably why this went unnoticed for a while. I added src/Tests/RootIndexComponentUnitTest.php covering both directions plus an update roundtrip against a real route; on the unpatched code the page-component test 500s with Unable to find component: [] and the name-based one passes.

Full Unit suite is green (1420 tests).

Unrelated to this PR, but I noticed it while poking at the same function: the namespace match is a plain startsWith, so it isn't dot-boundary aware. With a class location of App\Livewire, a class in a sibling namespace like App\LivewireForms\ContactForm matches and gets its prefix chewed off mid-segment, yielding forms.contact-form. Separate issue, leaving it alone here.

lazerg and others added 2 commits August 3, 2026 16:03
…ing an empty name

Fixes the same bug further upstream. The ordering was the actual problem: '.index'
was stripped from the fully-qualified name before the namespace prefix was removed,
so an 'Index' at the root of the namespace collapsed into the namespace itself and
left nothing behind.

Doing it in the other order means the empty name is never produced in the first
place, so there's nothing to restore, and a genuinely empty name is never silently
promoted to 'index'.

Also adds coverage for the same case under a registered namespace (which returned
'admin::' before), and an end-to-end test rendering a root-level Index through a
real route.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@joshhanley
joshhanley force-pushed the fix/root-index-component-name branch from fb28afa to 5ac7ece Compare August 3, 2026 06:03
@joshhanley

Copy link
Copy Markdown
Member

I've rebased this on the 4.x and retargeted the PR to 4.x branch

@joshhanley
joshhanley merged commit c1c2b51 into livewire:4.x Aug 3, 2026
ghabriel25 pushed a commit to ghabriel25/livewire that referenced this pull request Aug 3, 2026
…10475)

Co-authored-by: Caleb Porzio <calebporzio@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
ghabriel25 pushed a commit to ghabriel25/livewire that referenced this pull request Aug 3, 2026
…10475)

Co-authored-by: Caleb Porzio <calebporzio@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
ghabriel25 pushed a commit to ghabriel25/livewire that referenced this pull request Aug 3, 2026
…10475)

Co-authored-by: Caleb Porzio <calebporzio@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Component with name Index causes ComponentNotFoundException

3 participants