Components in sub-folders

I’ve this scaffolding

...
/components
  /card
     body.blade.php
     card.blade.php
     header.blade.php
  alert.blade.php
...

In the templates I include this <x-card>...</x-card> but I get error:
# Unable to locate a class or view for component [card].

Instead if I use <x-card.card>...</x-card.card> it works, anyway the official Laravel docs (here) says this <x-card>... should work

Anyone could help me on this? Thanks

Are these anonymous view components or do they have classes? I believe the latter is required for this to work, but I haven’t tried this before.

Hi @Log1x
don’t know exactly what you asked me. The card.blade.php component is like this:

@props([
  'as' => 'div',
  'href' => null,
])

@php
  $tag = $href ? 'a' : $as;
  $baseClass = 'card group block rounded-3xl overflow-hidden transition-shadow duration-300 hover:shadow-xl';
@endphp

<{{ $tag }} {{ $attributes->merge([
  'href' => $href,
  'class' => $baseClass
]) }}>
  {{ $slot }}
</{{ $tag }}>

Unless you have a app/View/Components/Card/Card.php class along with that View, then it is considered an anonymous component which from what I can tell doesn’t support “Index Components” from your docs link.

EDIT: Whoops, actually it does, just a different structure putting card.blade.php outside of the card directory. See the section right below the section I linked above: Anonymous Index Components

In that docs section below it says:

Thankfully, Blade allows you to place a file matching the component’s directory name within the component’s directory itself. When this template exists, it can be rendered as the “root” element of the component even though it is nested within a directory. So, we can continue to use the same Blade syntax given in the example above; however, we will adjust our directory structure like so:

/resources/views/components/accordion/accordion.blade.php
/resources/views/components/accordion/item.blade.php

So it should be possible but doesn’t work.

I also tried to add the app/View/Components/Card/Card.php class with command acorn make:component Card/Card:

<?php

namespace App\View\Components\Card;

use Closure;
use Illuminate\Contracts\View\View;
use Illuminate\View\Component;

class Card extends Component
{
    /**
     * Create a new component instance.
     */
    public function __construct()
    {
        //
    }

    /**
     * Get the view / contents that represent the component.
     */
    public function render(): View|Closure|string
    {
        return view('components.card.card');
    }
}

but again, doesn’t work, same error:
# Unable to locate a class or view for component [card]

Maybe I need some other actions? Launching some commands (clear cache) or something?

So you moved resources/views/components/card/card.blade.php to resources/views/components/card.blade.php like suggested in my edited docs link above?

I think creating a component class just for this is pretty overkill and I’d definitely recommend simply doing the above, but since you’re flipping back and forth between component types, you might need to run wp acorn optimize:clear for the View component class to get discovered. If you closely follow the Laravel docs, this should all otherwise work as expected.

Moving card.blade.php to /components works, it’s the default, it always worked.

I would have liked to put it under /components/card to better organize that component.
Anyway it’s not a big issue, but it’s strange that it doesn’t work since the Laravel docs says it should work under /components/card:face_with_diagonal_mouth:

About the class I’m with you, I’d like to avoid it if possible, since all my components are just views (hope this is a right way to use them).

About the class I’m with you, I’d like to avoid it if possible, since all my components are just views (hope this is a right way to use them).

As per the Laravel docs, you have to have the class to organize it how you want. If you don’t have a class for the view component, the only option is to keep the view in the root component directory like resources/views/components/card.blade.php.

Just to clarify, there’s 2 different sections on the Laravel docs for this.

One is how to handle index components for a “normal” view component which have a class (which you originally linked): Blade Templates - Laravel 12.x - The PHP Framework For Web Artisans

The other is how to handle index components for an anonymous view component which only consists of a view (like you’re doing): https://laravel.com/docs/12.x/blade#anonymous-index-components

That being said, the behavior you’re seeing is expected as per the documentation. If you try creating a component with a class again and run into an error, try running wp acorn optimize:clear and/or composer dump-autoload.

Maybe I’m wrong but this is from the docs for Anonymous Index Components

Thankfully, Blade allows you to place a file matching the component’s directory name within the component’s directory itself. When this template exists, it can be rendered as the “root” element of the component even though it is nested within a directory. So, we can continue to use the same Blade syntax given in the example above; however, we will adjust our directory structure like so:
/resources/views/components/accordion/accordion.blade.php
/resources/views/components/accordion/item.blade.php

Isn’t it saying that I can put accordion.blade.php in the subfolder /components/accordion and including with <x-accordion> and not <x-accordion.accordion>?

That being said, the behavior you’re seeing is expected as per the documentation. If you try creating a component with a class again and run into an error, try running wp acorn optimize:clear and/or composer dump-autoload .

I tried again with the class (code posted above); the view is in components/card/card.blade.php and included as <x-card>; launched those 2 commands. Same error as before.

You’re right! I was completely thrown off by how that section started and didn’t realize under the initial explanation they said that the original folder-based method should work as well.

I went ahead and gave it a test in my project creating resources/views/components/test/test.blade.php and was able to call <x-test /> without any issue.

Are you on the latest version of Acorn (5.x)? If not, I suspect that might be the culprit with this functionality having been originally added in Laravel 11.x.

Ouh, great, I was afraid I wasn’t understanding :slight_smile:

I’m working on Sage 10 (no Bedrock, etc) and:

php vendor/bin/acorn --version
Laravel Framework Acorn 4.3.1 (Laravel 10.43.0)

So you think the problem is the old version of acorn? Is there a way to use v5 without upgrading Sage? I’m not ready for Sage 11 / Tailwind 4.
Thanks

You should be able to just run composer require roots/acorn:^5.0.0 -W and then update your Sage’s functions.php to use sage/functions.php at 5ffb5636b2eae99afd6522eaba7e7809d8b939f5 · roots/sage · GitHub instead of the bootloader() function.