Laravel 8: Simple, Yet Powerful Alert Component

Using Laravel in a side project recently, the introduction of Blade Components has been a literal gift for me. Creating reusable components is something we all want in our work process. Thankfully, as of Laravel 7+, you can do just that with a simple artisan command.

Let’s make a basic “Alert” component, then build on it to make it powerful.

Step 1: Create the component

php artisan make:component Alert

Here, Laravel will create two new files:

  • App\View\Components\Alert.php
  • resources\views\components\alert.blade.php

You have a PHP component that collects data, and a view that delivers data. Simple!

Let’s look at the newly generated PHP file:

<?php

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\Contracts\View\View|string
*/
public function
render()
{
return view('components.alert');
}
}

Let’s clean it up a bit, reducing redundancy and introducing semantics:

<?php

namespace
App\View\Components;

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

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

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

We’ve cleaned up some redundancy and properly typed our render() method with View — Semantics are important after all. Not necessary, but it’s considered good practice to strictly declare your return types.

Step 2: Create Some Properties

An alert usually has two distinctive properties:

  • Level: i.e., Info, Success, Error, Warning
  • Message: i.e., “Successfully registered!”

With that in mind, let’s create those properties in our PHP file. Inside the class, update it with two new public properties:

$level and $message — both of which can now be accessed within the corresponding blade template.

class Alert extends Component
{
/**
* The priority of the alert, i.e., "info", or "warning"
*
*
@var string
*/
public
$level;

/**
* The message or an array of messages to present to the user
*
*
@var mixed
*/
public
$message;

/**
* Create a new component instance.
*
*
@param string $level
*
@param mixed $message
*/
public function
__construct(string $level, $message)
{
$this->level = $level;
$this->message = $message;
}

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

You may have noticed that the second parameter $message in our constructor is “mixed”. Error messages may not only contain a string, but an array of strings for multiple messages. This is, by definition, a mixed property.

Step 3: Building a dynamic component template

The proponent of features that come out of the box with Laravel’s Component system are something to be reckoned with. First, let’s build a simple template. In our alert.blade.php file, add:

<div class="c-alert c-alert--{{ $level }}" role="alert">
<p class="c-alert__message">{{ $message }}</p>
</div>

Notice what is happening here?

$level and $message are both accessible properties from the PHP component without passing them in to the second parameter of view() from within the render() method (similar to how you would pass data from within a controller, i.e., view('page.page-show', ['page' => $page])) — cool, right?!

Now, anywhere in your blade templates, (presumably your default layout template), you can put the following:

<x-alert level="info" message="Please try again." />

This would render the following HTML:

<div class="c-alert c-alert--info" role="alert">
<p class="c-alert__message">Please try again.</p>
</div>

You can now easily implement the <x-alert /> component anywhere in your blade templates.

Step 4: Making our component dynamic

Now we have created a simple Laravel component, we need to address a couple of issues within our template and make it a bit more dynamic.

The issues are:

  • No check if message or level is empty. This will throw a BindingResolutionException error if the either declaration is empty.
  • There’s no way to add additional class selectors to our existing selectors of c-alert c-alert--{{ $level }} if we wanted to.
  • There’s no way of adding additional HTML within our parent HTML element.

We can, fortunately, address all these issues thanks to some smart interpolation and handy variables.

Addressing each issue all at once

To solve each of the above, we can make some simple changes to our PHP and Blade template.

Back in our PHP file, let’s update the constructor:

public function __construct(string $level = 'info', $message = [])
{
$this->level = $level;
$this->message = $message;
}

And in the component blade template:

<div {{ $attributes->merge([ 'class' => 'c-alert c-alert--' . $level ]) }} role="alert">
@if (is_array($message))
{{-- Loop through an array of messages --}}
<ul class="c-alert__message">
@foreach($message as $item)
<li>{{ $item }}</li>
@endforeach
</ul>
@else
{{-- Display string message --}}
<p class="c-alert__message">{{ $message }}</p>
@endif

{{-- Allow additional HTML --}}
{{ $slot }}
</div>

So now, we can do the following:

<x-alert />

And the $level variable will resolve to a class name of c-alert--info but we will now have an empty message. This now:

  • Eliminates the BindingResolutionException problem when the properties are blank by using default values within the __construct() method
  • $attributes->merge() allows us to add new class declarations whilst persisting the existing declarations
  • The @if checks are used to determine an array or string $message value
  • {{ $slot }} allows us inject custom HTML directly in to the template.

Here’s an example:

<x-alert message="Sign up now!" class="mb-4" id="MyAlert">
<a href="#" class="c-btn c-btn--secondary">Register</a>
</x-alert>

Will render:

<div id="MyAlert" role="alert" class="c-alert c-alert--info mb-4">
<p class="c-alert__message">Sign up now</p>
<a href="#" class="c-btn c-btn--secondary">Register</a>
</div>

Notice the extra mb-4 class, additional element properties and injected HTML?

Step 5: PHP variables as properties

Now we have a dynamic component, how do we pass PHP variables to it? Take for example, we have a session flash that returns an $alert object with two properties — an array: message and a string: level— you would declare it like so:

@if (session()->has('alert'))
<x-alert :message="$alert->message" :level="$alert->level" />
@endif

Notice the semicolons? This might be familiar to you is you use Vue. In fact, it’s the same principle: If you need to interpolate a PHP variable, you use :propertyName instead of just propertyName — cool, isn’t it?! 😉

Here’s a quick example you can drop right in:

@php
$alert = (object)[];
$alert->message = ['Hello', 'World'];
$alert->level = 'warning';
@endphp
<x-alert :message="$alert->message" :level="$alert->level" />

Will render:

<div role="alert" class="c-alert c-alert--warning">
<ul class="c-alert__message">
<li>Hello</li>
<li>World</li>
</ul>
</div>

Bonus: Give it some style 😎

We’ll use SASS (SCSS) for styling up our component.

Create a new SCSS file and import it in to your base stylesheet. I use BEM methodology, and name my files consistently using namespaces, i.e.,

scss/components/_component.alert.scss — Component

scss/styles.scss — Base

Then you import it to your Base stylesheet using:

@import "components/component.alert";

Here’s the full declaration:

/*-----------------------------------------------------------------
* Alert
*-----------------------------------------------------------------
*
*-----------------------------------------------------------------
*
* A way of highlighting informative information by defining color
* codes, i.e., `danger` or `info` to differentiate them.
*
*-----------------------------------------------------------------
*
* [1] Remove margin from the content to keep spacing consistent.
* [2] Modifier declaration for informative messages.
* [3] Modifier declaration for successful messages.
* [4] Modifier declaration for critical messages.
* [5] Modifier declaration for warning messages.
* [6] Modifier declaration to align the message text center.
* [7] Support for placing icons inline with the message.
*
*---------------------------------------------------------------*/

//-----------------------------------------------------------------
// Defaults
// ----------------------------------------------------------------

$alert-namespace : 'c-' !default;
$alert-spacing : 24px !default;
$alert-spacing : $alert-spacing / 2 !default;
$alert--success : #3dee06 !default;
$alert--information : #0F35FF !default;
$alert--danger : #ef1e08 !default;
$alert--warning : #ffc01f !default;

//-----------------------------------------------------------------
// Declaration
// ----------------------------------------------------------------

.#{$alert-namespace}alert {
border : 1px solid lighten($alert--information, 10%);
background-color : $alert--information;
color : lighten($alert--information, 40%);
padding : $alert-spacing / 2;

/* [1] */
> p,
&__message {
display : inline-block;
margin : 0;
}

/* [2] */
&--info {
background-color : $alert--information;
color : lighten($alert--information, 40%);
border-color : lighten($alert--information, 10%);
}

/* [3] */
&--success {
background-color : $alert--success;
color : lighten($alert--success, 40%);
border-color : lighten($alert--success, 10%);
}

/* [4] */
&--danger {
background-color : $alert--danger;
color : lighten($alert--danger, 40%);
border-color : lighten($alert--danger, 10%);
}

/* [5] */
&--warning {
background-color : $alert--warning;
color : lighten($alert--warning, 40%);
border-color : lighten($alert--warning, 10%);
}

/* [6] */
&--center {
text-align: center;
}

/* [7] */
&__icon {
padding-right: $alert-spacing / 3;
}
}

You can now fully customize your component by overriding the !default values above your @import statement, for example:

$alert-namespace     : 'devjmd-';
$alert-spacing : 12px;
@import "components/component.alert";

This would now generate:

.devjmd-alert {
padding: 6px;
...
}

.devjmd-alert--info {
...
}

… and so forth.

Final Words

There’s so much more to Laravel’s component layer. In fact, this very guide is based on the documentation version, except with a bit more control over the type of data you want to show.

I highly recommend reading their official documentation on Blade Components: https://laravel.com/docs/8.x/blade#components — it’s worth the investment if you’re managing a large project.

On another side note, you can use these components alongside Vue.js, but be warned, this could get complicated in the long run. Make sure you’re using Blade Components wisely, and only where necessary where duplication becomes obvious. If you’re worried about Server-side Rendering, then Blade Components are absolutely the way to go as they’re rendered on the back-end anyway. Maybe this is a valid reason to use the two in conjunction.

The way to use Blade Components along side Vue, is by excluding the x- tag from Vue’s tag collection — that I will leave up to you to figure out! 😉

Enjoy! 😁

A full-stack developer based in North Yorkshire, UK. 10+ years experience in multiple programming languages. Loves architecture principles and design.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store