Today we launched our newest package called BladeX. In short this package provides you with an easy html like way to render custom html components in your Blade views. In this blogpost I'd like to introduce the package to you.

A first example #

When building a server rendered app you're probably going to wind up with various pieces of html that you want to reuse. Think of things like form components, alerts and entire layouts. There are various options to make such html reusable. You can simply create your own custom function, or use an html builder, or use Blade's @include or @component directives.

Even though IT is sometimes seen as an exact science, it seems like every programmer has his/her own personal preference on how to deal with html. In my opinion al the options mentioned above are valid. In most of our own projects at Spatie we have used a combination of them all.

In particular we like Blade's @component directive. Inspired by Vue, a Blade component is an awesome way to reuse html. Here's a quick example on how to use it. Let's create an alert component!

{{-- resources/views/components/myAlert.blade.php --}}
<div :class="$type">
   {{ $message }}
</div>

Using the @component directive you can use that component like this.

{{-- in a view --}}
@component('components/myAlert', [
   'type' => 'error',
   'message' => $message
])
@endcomponent

That directive isn't that hard to use but it is very PHP like. Wouldn't it be nice if we could use that myAlert component just like we are able in Vue with an html like syntax?

With our BladeX package installed you can use the myAlert component like this:

<my-alert type="error" :message="$message" />

Using an html like syntax you'll need less characters and (in my opinion) the template becomes more readable.

Using variables #

When using a BladeX component all attributes will be passed as variables to the underlying Blade view.

{{-- the `myAlert` view will receive a variable named `type` with a value of `error` --}}
 
<my-alert type="error">

If you want to pass on a php variable or something that needs to be evaluated you must prefix the attribute name with :. We borrowed that syntax from Vue.

{{-- the `myAlert` view will receive the contents of `$message` --}}
<my-alert type="error" :message="$message">

{{-- the `myAlert` view will receive the uppercased contents of `$message` --}}
<my-alert type="error" :message="strtoupper($message)">

Using slots #

BladeX support slots too. This layout component

{{-- resources/views/components/layout.blade.php --}}

<div>
    <h1>{{ $title }}</h1>
    <div class="flex">
        <div class="w-1/3">
            {{ $sidebar }}
        </div>
        <div class="w-2/3">
            {{ $slot }}
        </div>
    </div>
    <footer>
        {{ $footer }}
    </footer>
</div>

can be used in your views like this:

<layout title="Zed's chopper">
    <slot name="sidebar">
        <ul>
            <li>Home</li>
            <li>Contact</li>
        </ul>
    </slot>

    <main class="content">Whose motorcycle is this?</main>

    <slot name="footer">It's not a motorcycle honey, it's a chopper.</slot>
</layout>

Prefixing components #

A few fellow devs have voiced their concern that if you're using Vue of React it might be hard to distinguish BladeX components from JavaScript components. If you share that concern, you'll be happy to learn that the package allow you to globally set a prefix for BladeX components.

Setting a global prefix is easy.

BladeX::component('my-alert', 'components.myAlert');

BladeX::prefix('x');

All your registered components can now be used like this:

<x-my-alert message="Notice the prefix!" />

How all this works under the hood #

To actually use a BladeX component you first have to register it. You can do this in the AppServiceProvider or a service provider of your own.

BladeX::component('my-alert', 'components.myAlert')

In the Spatie\BladeX\BladeXServiceProvider, which is loaded automatically when the package is installed we extend Blade's default compiler with our own BladeXCompiler. The BladeXCompiler loops over all registered components. That class contains two regexes, carefully crafted by Alex and Seb, to replace BladeX component usages such as this

<my-alert type="error" :message="$message" />

to the the equivalent code using Blade's @component directives.

@component('components/myAlert', [
   'type' => 'error',
   'message' => $message
])
@endcomponent

After that, Blade will kick in and parse the html as usual. Caching views will just work, so there isn't a performance penalty for this at all. Because all this happens before any html is sent to the browser, there aren't any interop issues with client side frameworks like Vue.js.

How we made this #

I'm proud of the features our package brings, but I'm equally proud of how we made this package. The problem of html generation was discussed during a lunch on Friday. In that same discussing the idea sprouted to build BladeX. At our company everybody is allowed some time to work on open source. It so happened that everybody still have some open source time left for that week.

Nearly the entire development team sat down the whole afternoon in our meeting room to build the package. Even though most packages are team work, most of them have a principal author and the rest of the team polishes the produced code. BladeX was a real team effort where all authors contributed while the package was being created. It was a really fun experience and I hope we're going to create future packages this way again.

Normally we give our packages dry, descriptive names like laravel-permission, laravel-medialibrary or laravel-backup. If we would follow that line of naming BladeX should have been called laravel-html-components or something similar. During development Alex came up with the name BladeX. It sounds so badass that we decided to stick with it.

Closing thoughts #

v1.0.0 is tagged but we aren't going stop development here. In the latest North Meets South podcast Jacob and Michael discussed BladeX and shared some interesting ideas for it. We are going to investigate some integration with our laravel-view-components and/or Laravel's native view composers to easily transform and fetch data before it gets passed into the component.

If you like BladeX go take a look at all the other open source packages our team has made previously. If you like our work, consider supporting us via Patreon.

I'm planning on writing a blogpost soon on how to build common crud interface in a modern way. That post will surely feature BladeX, laravel-query-builder, laravel-view-models and a few of our other packages.

If you have any question on BladeX feel free to use the comments below or open up an issue at the repo on GitHub.