Tinkerwell Logo

It's time to Tinkerwell!

Go back to Blog

Using Laravel View Components

Marcel Pociot

Using Laravel View Components

When Laravel 7 was released, it introduced a new feature how you can organise and structure your blade views, called Blade components.

If you've been around in the Laravel community you might have heard the term "Blade component" before - actually the whole concept of separating your views into individual components is not particularly new to Laravel 7. But Laravel 7 introduced a new, much more improved way of how we can make use of these components.

In this blog post series, we will first take a look at what a Blade component actually are, how we can use them, and then see how we can transform our "traditional" Blade views to make use of our components.

What are Blade view components?

As mentioned above, the concept of a view component is nothing new in the Laravel world. Prior to version 7, you could create a custom component, let's say an alert, and place it in resources/views/alert.blade.php. Such a component could look like this:

<div class="alert alert-danger">
    {{ $slot }}
</div>

To make use of this component inside of our other views, we were able to use the @component directive, like this:

@component('alert')
    <strong>Whoops!</strong> Something went wrong!
@endcomponent

This would render the alert.blade.php file, and automatically inject the inner content of our @component and @endcomponent directives within the $slot variable.

The downside of this approach was that you would mix your semantic HTML structure with pretty verbose @component tags. It also makes it harder to parse which component is actually being used.

Semantic components

This changed with the release of Laravel 7, so let's take a look at how our alert component could look like with the latest version of Laravel.

The component itself would live inside a resources/views/components/alert.blade.php file. So by default, all view components live inside the components directory within your views. The content of our alert component itself remains the same:

<div class="alert alert-danger">
    {{ $slot }}
</div>

If this pretty much remained the same, what's the big difference? Well - instead of having to use these ugly @component directives, we can now use semantic names for our components:

<x-alert>
	<strong>Whoops!</strong> Something went wrong!
</x-alert>

Now look at this! It might not seem like a big difference, but simply the fact that we can use HTML-like syntax for our custom components already makes this much easier to read and parse, when scanning through your view files.

All custom blade components get prefixed with x-, followed by the component name. Just like the $slot variable got populated with the directives, Laravel automatically populates the $slot variable with everything that sits between your opening and your closing component tag.

Semantic HTML? That's it?

At this point, you might be wondering, if the Blade components are just a fancy way of doing @component or @include - but there is a lot more to it.

The alert example above is what Laravel calls an "anonymous" component. This means that the component only consists of a Blade view and not a standalone PHP class that can be used along with it.

Let me give you an example of how such a class can look like. To generate such a view component, you can call php artisan make:component Alert. This will generate two files for you:

  • resources/views/components/alert.blade.php
  • app/View/Components/Alert.php

If we take a look at our Alert.php file, this is its content:


namespace App\View\Components;

use Illuminate\View\Component;

class Alert extends Component
{
    /**
     * Create a new component instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Get the view / contents that represent the component.
     *
     * @return \Illuminate\View\View|string
     */
    public function render()
    {
        return view('components.alert');
    }
}

You can think of this a mini-controller that only invokes the rendering of your Alert component - and out of the box it doesn't do anything special.

But we can make use of our Alert component class in order to provider methods and data to our alert view, that is only relevant for this particular view.

Let's say that you want to distinguish between different alert styles: success, info, warning, and error.

By adding a constructor parameter to our view component class, Laravel now knows which data our component requires.

So let's change the way that we build our component and provide a type along with it:

<x-alert type="success">
	Yay!
</x-alert>

Just like you would pass properties to your Vue/React components, you can pass properties to your Blade components. As the type variable is available within your component class constructor, Laravel knows which variable you are passing to the component. By default, all public properties (and methods) will be available within your Blade view - so we can access our new $type property within our view:

<div class="alert alert-{{ $type }}">
    {{ $slot }}
</div>

Depending on the type that you pass to the component, this is now going to change its class attribute and prefix it with alert-.

Passing HTML attributes

By introducing the type attribute, we are able to pass data from our view to our view components. This is great if you want to pass down custom data, but how about passing down attributes that you want to use within your HTML or Javascript? What if you want to provide a custom id attribute, or custom aria- attributes? Adding all of these attributes to a component class constructor is definitely very cumbersome. That's why Laravel gives you a better alternative for this.

All attributes that you pass to a component, that are not explicitly set in your view component class, are available within a special $attributes variable. You can output all of the passed attributes like this:

<div {{ $attributes }} class="alert alert-{{ $type }}">
    {{ $slot }}
</div>

So if you would call the alert component like this:

<x-alert id="my-alert" role="alert" type="success">
	Uh oh!
</x-alert>

We would get an output like this:

<div id="my-alert" role="alert" class="alert alert-success">
	Uh oh!
</div>

Merging attributes

So far, our Alert component dynamically builds its CSS class list. In case of a error type, it would output class="alert alert-error", but what if you want to add custom classes to this list? With the $attributes variable, we already have the ability to add any arbitrary attribute to our components - but we can also make use of this $attribute variable and use it to merge a specific attribute with certain default values:

<div {{ $attributes->merge([
  'class' => 'alert alert-'.$type
  ]) }}>
    {{ $slot }}
</div>

This is now going to take any attribute that is being passed to the component, and for the special class attribute, we are applying the "alert" and "alert-$type" values as defaults. So whenever you call the component with a custom class attribute, those attributes will be merged and added to the default classes.

Component methods

As I said above, not only all public properties are made available within your component, but also all of your public methods. This allows us to add custom logic that is only required within our component, to the component class itself.

For example, you can create a Markdown component, that takes a string of markdown within its slot, and then renders it.

Before we take a look at any actual code, lets see how we would like to call such a component:

<x-markdown>
# This is cool!

**Markdown** rendered from our custom _component_!
</x-markdown>

This means that our Markdown component needs to render the $slot variable as HTML.

Let's see how we can make this work. First, we will generate a new component using php artisan make:component Markdown. In our Markdown component class, we will add a new method called parseMarkdown, that will take a markdown string and return the HTML representation of it. For the sake of simplicity, we are going to reuse the Illuminate\Mail\Markdown class, which provides markdown parsing out of the box.

namespace App\View\Components;

use Illuminate\View\Component;
use Illuminate\Mail\Markdown as MarkdownParser;

class Markdown extends Component
{
    public function parseMarkdown($markdown)
    {
        return MarkdownParser::parse($markdown);
    }

    public function render()
    {
        return view('components.markdown');
    }
}

Alright - so our view component class now holds the logic of how the view can convert a markdown string to HTML. In order to use this method within our component, we can simply call it. The specialty here is that the parseMarkdown method is being passed to the view as a variable. Our view content then looks like this:

<div>
    {!! $parseMarkdown($slot) !!}
</div>

This way, we can keep our view really clean, while still being able to encapsulate all of the logic that our view requires within its accompanying class file.

What's next?

I hope that this gave you a good overview of what can be done with a Blade view component. In the next post we are going to look at how you can leverage these view components within your views and how to refactor your existing views to individual, reusable components.