Muhammad Yasir Amin

Loading websiteโ€ฆ

More from Muhammad Yasir Amin

Build Along With Me

Practical walkthroughs on Laravel, React, APIs and deployment workflows.

Subscribe on YouTube

Laravel 13 Send Email via Notification: MailMessage, Queues and On-Demand Routing

๐Ÿ› ๏ธ Laravel Tutorials5 min read

Laravel 13 Send Email via Notification: MailMessage, Queues and On-Demand Routing

Muhammad Yasir Amin

Muhammad Yasir Amin

Senior Full Stack Engineerโ€ขNov 12, 2026
Laravel 13 Send Email via Notification: MailMessage, Queues and On-Demand Routing

To send an email via a notification in Laravel 13, generate a notification class with php artisan make:notification, return ['mail'] from its via() method, build the message in toMail() with a MailMessage, and call $user->notify(new YourNotification()). Implement ShouldQueue on the class so the email is sent in the background.

Notifications are my default for short, transactional emails like "your order shipped" or "your invoice was paid". You write the content once, and later you can add Slack, SMS or database delivery to the same class without touching the code that triggers it.

Prerequisites

  • Laravel 13.x on PHP 8.3 or newer.
  • A working mailer in .env. During development, MAIL_MAILER=log writes emails to storage/logs/laravel.log instead of sending them.
  • A queue connection if you want background delivery. The default database driver is fine to start.
  • A notifiable model. The default User model already uses the Notifiable trait.

Step 1: Generate the notification

Code
php artisan make:notification OrderShipped

This creates app/Notifications/OrderShipped.php.

Step 2: Build the mail message

Pass the data the email needs through the constructor. Laravel serializes Eloquent models in queued notifications by their ID and reloads them when the job runs, so passing a model is cheap.

JavaScript
<?php

namespace App\Notifications;

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

class OrderShipped extends Notification implements ShouldQueue
{
    use Queueable;

    public function __construct(public Order $order)
    {
        $this->afterCommit();
    }

    /**
     * @return array<int, string>
     */
    public function via(object $notifiable): array
    {
        return ['mail'];
    }

    public function toMail(object $notifiable): MailMessage
    {
        return (new MailMessage)
            ->subject("Order #{$this->order->id} is on its way")
            ->greeting("Hi {$notifiable->name},")
            ->line('Good news: your order has left our warehouse.')
            ->lineIf($this->order->tracking_number !== null, "Tracking number: {$this->order->tracking_number}")
            ->action('Track your order', route('orders.show', $this->order))
            ->line('Thanks for shopping with us.');
    }
}

A few notes on the builder methods:

  • subject() overrides the default subject, which is the class name in title case ("Order Shipped").
  • action() renders a button. Call ->error() on the message to render it red for failure emails.
  • lineIf() adds a line only when the condition is true, which keeps the method free of if-blocks.
  • afterCommit() in the constructor makes the queued job wait until any open database transaction commits, so the worker never reads an order that was rolled back.

Step 3: Send the notification

Call notify() on any model that uses the Notifiable trait:

SQL
$order->user->notify(new OrderShipped($order));

To notify several users at once, use the facade:

PHP
use Illuminate\Support\Facades\Notification;

Notification::send($admins, new OrderShipped($order));

Because the class implements ShouldQueue, both calls push a job to the queue and return immediately. Start a worker to process it:

Code
php artisan queue:work

How do I send a notification to an email address that is not a user?

Use an on-demand notification. This is handy for guest checkouts or a fixed operations mailbox:

JavaScript
use Illuminate\Support\Facades\Notification;

Notification::route('mail', ['ops@example.com' => 'Operations'])
    ->notify(new OrderShipped($order));

Inside toMail(), $notifiable is then an AnonymousNotifiable without a name property, so guard any user-specific fields when a notification can be sent both ways.

How do I change which address the email goes to?

By default the mail channel uses the model's email attribute. Override it on the model with routeNotificationForMail():

JavaScript
use Illuminate\Notifications\Notification;

public function routeNotificationForMail(Notification $notification): array|string
{
    return [$this->billing_email ?? $this->email => $this->name];
}

Can I use my own email design?

Yes. Return (new MailMessage)->markdown('mail.order.shipped', ['order' => $this->order]) to render a Markdown template with Laravel's mail components, or ->view() for a plain Blade view. You can also return a full Mailable from toMail() if you already have one.

What does the default notification email look like?

Laravel renders MailMessage content into a responsive HTML template with a header showing your APP_NAME, the greeting, your lines, a centered button for the action, a closing "Regards" line and a small footer with the plain action URL for clients that block buttons. A plain-text version is generated automatically. To change the colours, logo or footer for every notification, publish the templates with php artisan vendor:publish --tag=laravel-notifications and php artisan vendor:publish --tag=laravel-mail, then edit the files under resources/views/vendor. Keep the content short: notifications work best when the email has one clear purpose and one action.

Common mistakes

  • No queue worker running. With ShouldQueue and no worker, nothing is sent. Check the jobs table, and failed_jobs for errors.
  • Stale code in workers. Workers keep the app in memory. Run php artisan queue:restart after each deployment.
  • Wrong APP_URL. Links built with route() inside queued notifications use APP_URL, so set it correctly in production.
  • Heavy work in the constructor. Compute values in toMail(), which runs on the worker, not in the request.

How to verify it works

For a quick manual check, set MAIL_MAILER=log, trigger the notification and read the rendered HTML in the log. For automated checks, fake notifications in a Pest test:

JavaScript
<?php

use App\Models\Order;
use App\Notifications\OrderShipped;
use Illuminate\Support\Facades\Notification;

it('notifies the customer when an order ships', function () {
    Notification::fake();

    $order = Order::factory()->create();

    $this->post(route('orders.ship', $order))->assertRedirect();

    Notification::assertSentTo(
        $order->user,
        OrderShipped::class,
        fn (OrderShipped $notification) => $notification->order->is($order),
    );
});

For on-demand recipients use Notification::assertSentOnDemand(OrderShipped::class).

FAQ

Notification or Mailable: which should I use?

Use a notification for short transactional messages that might later go to more channels. Use a Mailable for richly designed emails, newsletters or messages with many attachments.

Can I delay a notification email?

Yes. Call ->delay(now()->addMinutes(10)) on the notification instance before passing it to notify().

Can I send to mail and database at the same time?

Return ['mail', 'database'] from via() and add a toArray() method for the database payload.

References

Article Index & Topic Tags
LaravelEmailNotificationsQueues

YouTube Channel

Build Along With Me

Practical walkthroughs on Laravel, React, APIs and deployment workflows.

Muhammad Yasir Amin
Available for Contracts
Author & Solutions Architect

Senior Full Stack Engineer

Independent Senior Full Stack Engineer working on Laravel, React and Next.js applications, APIs and delivery workflows.

Laravel & PHP ApplicationsReact & Next.js InterfacesREST APIs & IntegrationsDatabase Queries & DebuggingDocker Development WorkflowsClear Scope & Handover
Discuss Your Next Step

What Does Your Application Need Next?

Share your current system, the problem you want to solve and what a useful result looks like. We can identify the right starting point and agree on a practical scope.

Technical Audit

Find the Next Useful Fix

Application Development

Scoped, Reviewable Milestones

Ongoing Support

Agreed Capacity and Priorities

Share your goals, current stack and requirements before the call.