Learn How to Send Beautiful Mail Notifications in Laravel

Notifications are informational messages delivered to app users. They are mostly meant to notify of certain transactions or events like password change, order status, and so on. Laravel provides a built-in feature called Notification. It allows you to send a message to any user via different communication channels. Today, we’ll focus on sending email notifications and explain how to implement this in your app.

What notification channels are supported by Laravel?

Although we’ll be talking about notifications to be sent via the email channel, you might be interested in other options. Besides mail, those include:

  • SMS – users will get text notifications on their phone. SMS notifications in Laravel are implemented through the Nexmo API. But you are free to use another SMS provider, of course:)
  • Slack – users will get notifications in Slack. For this, you need to configure the Incoming Webhooks integration to send data into Slack in real-time.
  • Database – users will get notifications in the app’s user interface. The information about notifications is stored in a database table. Your JavaScript client accesses it to return notifications for a notifiable user.
  • Broadcast – notifications are broadcast to a notifiable user in real time. When some data is updated on the server, a message is sent over a WebSocket connection to the JavaScript client.

If you need a custom notification channel, you can always write your own drivers to deliver notifications. But first, let’s find out how it goes with email notifications.

How to create a mail notification?

Notifications in Laravel are created with the make:notification Artisan command. Let’s make one on the order status update as follows:

php artisan make:notification StatusUpdate

All notifications are stored in the app/Notifications directory. Once created, you’ll see the following content in your StatusUpdate.php file:

<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;

class StatusUpdate extends Notification
{
    use Queueable;

    /**
     * Create a new notification instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Get the notification's delivery channels.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function via($notifiable)
    {
        return ['mail'];
    }

    /**
     * Get the mail representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return \Illuminate\Notifications\Messages\MailMessage
     */
    public function toMail($notifiable)
    {
        return (new MailMessage)
                    ->line('The introduction to the notification.')
                    ->action('Notification Action', url('/'))
                    ->line('Thank you for using our application!');
    }

    /**
     * Get the array representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function toArray($notifiable)
    {
        return [
            //
        ];
    }
}

Let’s format it as a message to be sent via the email channel. 

Notification formatting 

To preview all variations of the email, you need to return it directly from a route. So, you don’t have to actually send it to a real or fake user. For this, we need to create a route by adding the following line to web.php in the routes/ directory:

Route::get('send', 'NotifyController@index);

And here is how to preview your notification:

Route::get('mail', function () {
    $order = App\Order::find(1);

    return (new App\Notifications\StatusUpdate($order))
                ->toMail($order->user);
});

Each notification in Laravel is represented by a single class. If you want your app to send email notifications, focus your attention on two methods, via and toMail

The via method will receive a $notifiable entity and defines the notification delivery channel. In the code above, we already have the required parameter, return ['mail'], by default.

public function via($notifiable)
    {
        return ['mail'];
    }

toMail is a message building method triggered by the via method. It will receive a $notifiable entity and should return an Illuminate\Notifications\Messages\MailMessage instance. toMail allows you to configure email parameters. So, your mail message may contain a text line (line), a call-to-action button (action), a greeting line (greeting), and so on. Check out the following example:

public function toMail($notifiable)
{
  return (new MailMessage)
  ->greeting('Hello!') 
  ->line('Your order status has been updated')
  ->action('Check it out', url('/'))
  ->line('Best regards!');
}

Here, we defined several methods provided by the MailMessage object. The notification will be converted into a responsive email template consisting of HTML and plain-text. 

If you want to inform users of some failed activity or error, there is the error method for that. It will mark your email’s CTA button in red. Here is how it looks under the hood:

public function toMail($notifiable)
{
  return (new MailMessage)
  ->error()
  ->subject('Payment Failed')
  ->line('Your payment details are wrong')
  ->action('Pay', url('/'));
}

If you prefer to use a custom template to render the notification message, you can make use of the view method. It will specify a template to be used instead of defining the “lines” of text. For example:

public function toMail($notifiable)
{
  return (new MailMessage)->view(
  'emails.order', ['order' => $this->order]
  );
}

Don’t forget to put your template under resources/views/emails/order.blade.php

Another alternative is to generate a mailable class and return it from toMail as follows:

use App\Mail\StatusUpdate as Mailable;

public function toMail($notifiable)
{
  return (new Mailable($this->order))->to($this->user->email);
}

Customizing the email notification attributes

Sender

The address the email notification is sent from is defined by default in the configuration file – config/mail.php. If you want to use a specific address, it’s not necessary to tweak this file. You can specify it using the from method as follows:

public function toMail($notifiable)
{
  return (new MailMessage)
  ->from('sender@example.com', 'Sender')
}

Recipient 

The Laravel Notification system sends an email message to the recipient identified as a user of your app. But you can customize the recipient’s email address. For this, you need to define a routeNotificationForMail method on the notifiable entity. For example:

<?php

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
  use Notifiable;
  
  public function routeNotificationForMail($notification)
  {
    return $this->email_address;
  }
}

Starting from version 5.6.19, Laravel supports for multiple cc, bcc and reply-to recipients on mail notifications. This can be done on mailable classes as follows:

public function build()
{
  return $this->from('example@example.com')
  $this->cc('cc@example.com')
  $this->bcc('bcc@example.com')
  $this->replyTo('replyTo@example.com')
  ->view('emails.orders.shipped');
}

Subject

By default, the class name of the notification forms the email subject. But it’s automatically formatted to “title case”. In our example, we named the class StatusUpdate. So, the email subject will be Status Update. For a specific subject for your notification, you can use the subject method as follows:

public function toMail($notifiable)
{
  return (new MailMessage)
  ->subject('Order Status')
}

Here is the email notification we’ve got in the end:

<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;

class StatusUpdate extends Notification
{
  use Queueable;
  
  public function __construct()
  {
    //
  }
  
  public function via($notifiable)
  {
    return ['mail'];
  }
  
  public function toMail($notifiable)
  {
    return (new MailMessage)
    ->subject('Order Status')
    ->from('sender@example.com', 'Sender')
    ->greeting('Hello!') 
    ->line('Your order status has been updated')
    ->action('Check it out', url('/'))
    ->line('Best regards!');
  }
  
  public function toArray($notifiable)
  {
    return [
      //
    ];
  }
}

Once the notification is ready, let’s get it delivered!

How to send mail notifications?

Configuring the email sender

First, we need to set up a configuration to be used for sending emails. Laravel uses the popular SwiftMailer library. It has drivers for a bunch of popular email services like Mailgun, Amazon SES (check out our tutorial on how to use Amazon SES), and others. The configuration for the API-based drivers, you will find in the Laravel Documentation

If you prefer using a particular SMTP relay service for sending emails, you need to tweak configuration in the .env file in the root directory. For example, here are the settings for SendGrid SMTP server:

APP_NAME="YOUR_APP_NAME"

MAIL_DRIVER=smtp
MAIL_HOST=smtp.sendgrid.net
MAIL_PORT=25
MAIL_USERNAME=<username>
  MAIL_PASSWORD=<password>
    MAIL_ENCRYPTION=ssl
    MAIL_FROM_NAME=<Sender’s name>
    MAIL_FROM_ADDRESS=<Sender’s email address>

For more on this, read the tutorial on How to send emails in Laravel.

Sending notifications

There are two ways for sending mail notifications in Laravel: Notifiable trait and Notification facade. 

Sending with Notifiable trait 

Notifiable trait contains the notify method to send notifications. The App\User class implements the trait, and it becomes the notifiable entity. 

<?php

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
  use Notifiable;
}

notify expects to receive a notification instance:

use App\Notifications\StatusUpdate;

$user->notify(new StatusUpdate($order));

That’s how NotifyController.php will look if you use the Notifiable trait for notification sending:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\User;
use Notification;
use App\Notifications\StatusUpdate;
use Illuminate\Notifications\Notifiable;

class NotifyController extends Controller
{
  public function __construct()
  {
    $this->middleware('auth');
  }
  
  public function index()
  {
    $user->notify(new StatusUpdate($order));
  } 
}

You can use Notifiable trait not only on the App\User class but on any of your classes.

Sending with Notification facade

Facades in Laravel are sort of static methods to classes but with an expressive syntax, as well as better testability and flexibility. They allow you to use Laravel’s features, and you don’t have to remember long class names to be configured manually. The Notification facade is suitable for sending a notification to multiple notifiable entities. To use it, pass the notifiable entities and the notification instance to the send method as follows:

Notification::send($users, new StatusUpdate($order));

On-demand and queued notifications

In the above examples, notifications will be sent to users of your app. And what if you need to notify someone else? In this case, you can use the Notification::route method that will send the notification to the recipient specified by you. Here is how it looks:

Notification::route('mail', 'not-a-user@example.com')
->notify(new StatusUpdate($order));

Something else you might be interested in is queued emails. It’s not about SMTP queues, but queued notifications that will let you speed up your app’s response time. For this, you need to add the ShouldQueue interface and the Queueable trait to your class. You don’t have to import them since they already are for all notifications generated through make:notification. All you need to do is add them to your class:

<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;

class StatusUpdate extends Notification implements ShouldQueue
{
  use Queueable;
  
  // ...
}

After that, send the notification as usual, and its delivery will be queued automatically by the ShouldQueue interface. If you need to delay the delivery, there is the delay method for this. Chain it onto the instantiation of your notification like this:

$when = now()->addMinutes(10);

$user->notify((new StatusUpdate($order))->delay($when));

How to test email notifications

Mailtrap.io

Once you have your email notifications set up, you should test this functionality. And in Laravel, you don’t have to send actual notifications thanks to Mailtrap.io used as a default SMTP method. This service has a dummy mailbox underneath that traps the email notifications sent from your app. There is no need to play much with your app’s code. All you need to do is type in your Mailtrap credentials to the .env file, so it will look as follows:

MAIL_DRIVER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=<username> //your Mailtrap username 
  MAIL_PASSWORD=<password> // your Mailtrap password 
    MAIL_FROM_ADDRESS=from@example.com
    MAIL_FROM_NAME=Example

Alternatively, you can adjust your config/mail.php using the same settings. After that, all the email notifications sent from your app will end up in the Mailtrap Demo Inbox. Also, you’ll be able to check your notifications for spam and preview them to know how they are rendered in different email clients. For more on the available features and functionalities, read the Mailtrap Getting Started Guide.

Notification Fake

Besides Mailrap, you can make use of the fake method of the Notification facade. It mimics the mail delivery process and lets you verify whether a particular email would have hit the recipient’s inbox. Check out how it works:

<?php

namespace App\Notifications;

use App\User;
use App\Notifications\StatusUpdate;
use Illuminate\Support\Facades\Notification;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithoutMiddleware;

class StatusUpdate extends User
{
  public function testStatusOrder()
  {
    Notification::fake();
    
    // Perform status order...
    
    Notification::assertSentTo(
      $user,
      StatusOrder::class,
      function ($notification, $channels) use ($order) {
        // retrieve the mail content
        $mailData = $notification->toMail($user)->toArray();
        $this->assertEquals("Order #{$order->orderNumber}           shipped", $mailData['subject']);
        $this->assertEquals("Hello {$user->name}!", $mailData['greeting']);
        $this->assertContains("Your order #{$order->orderNumber} has been shipped", $mailData['introLines']);
        $this->assertEquals("View Order", $mailData['actionText']);
        $this->assertEquals(route('orders.show', $order), $mailData['actionUrl']);
        $this->assertContains("Thank You!", $mailData['outroLines']);
        return $notification->order->id === $order->id;
      }
    );
  }
}

Here, you can see various assertions to test the mail content. They are made after the code under test is executed.

Wrap up: How to get the most of the email notifications?

You should not go without email notifications in your app. They increase the LTV of your customers, as well as the retention rate. That’s why you must not only have them function well, but also make sure of their quality and validity. The following tips will help you get the most of email notifications:

  • Control spamminess of your notifications

We know you’re not a spammer (at least we hope so), but are spam filters aware of this as well? You should care about why emails going to spam and how to prevent it. This will prevent your users from missing some important notifications and will strengthen their loyalty to your app.

  • Polish your body text

The content of an email notification must be grammatically correct. This is one of the essential aspects of spam-proof emails, as well as a sign of respect to your users.

  • Put emphasis on the subject line

We’re sure you know the importance and significance of the email subject line. So, make it informative and eye-catching, and avoid such spam-triggers as exclamation marks or capital letters. Check out the Email Testing Checklist to discover some useful tools for handling email subject lines.

  • Apply a personal approach 

Most likely, this tip is better suited to email marketing campaigns. However, setting a personal connection via email notifications won’t do any harm. Instead, it’s a good way to show the importance of each user individually.

  • Use appealing layouts

People draw attention to visually appealing content. But you should be careful in visualizing your email notifications. Do not abuse colors and fonts. You need to attract the recipient with your message rather than make him want to close it right away.