skip to content

In Laravel, when would you send an email as a notification built with MailMessage rather than as a Mailable, and how can the two be combined?

level: middleimportance: must knowfreq 50%

answer

  1. one event, several channels
  2. greeting, line, action, error
  3. one call-to-action button
  4. subject defaults from the class name
  5. toMail() may return a Mailable plus ->to()

basics

~20 s

Use a notification when one event may reach a person over several channels and its email is a simple templated message built with MailMessage. Use a Mailable for a designed, email-only message. toMail() can return a Mailable, but then you set its recipient.

solid answer

~50 s

A **notification** models an event for a notifiable — "appointment tomorrow" — delivered over whichever channels `via()` picks, so email is one representation beside SMS and the in-app list. Its `toMail()` usually returns an `Illuminate\Notifications\Messages\MailMessage`, a fluent builder (`subject()`, `greeting()`, `line()`, `action()`, `error()`, `salutation()`) rendered through Laravel's notification template, with a single call-to-action button. That is quick and consistent but deliberately limited. A **Mailable** is an email-only class with its own envelope, view and attachments, sent with `Mail::to()`, suited to designed emails such as newsletters or receipts. They combine: `toMail()` may return a Mailable, keeping the notification's channel logic while using a custom template — but then you must set the recipient yourself with `->to($notifiable->email)`, because the mail channel does not address a Mailable. A `MailMessage` can also switch to your own template with `markdown()` or `view()`.

code

php · 13 lines
php
<?php

use Illuminate\Notifications\Messages\MailMessage;

public function toMail(object $notifiable): MailMessage
{
    return (new MailMessage)
        ->subject('Your appointment tomorrow')
        ->greeting("Hello {$notifiable->first_name},")
        ->line("Your check-up with Dr Okafor is at {$this->appointment->starts_at->format('H:i')}.")
        ->action('Confirm or reschedule', route('appointments.show', $this->appointment))
        ->line('Please arrive ten minutes early.');
}

go deeper

for a junior

Recall that a notification can reach several channels while a mailable is only email, and name the basic MailMessage methods: subject, greeting, line and action.

for a middle

Explain MailMessage's limits — one action, fixed layout, subject from the class name — and how returning a Mailable from toMail() works, including setting ->to().

for a senior

Show you choose per message: channel-spanning events as notifications, designed or attachment-heavy mail as mailables, and a returned Mailable when both needs meet.

for a principal

Set a team convention for which messages are notifications, which are mailables, and who owns the shared templates, so branding and channel preferences stay consistent.

## Two abstractions for two jobs Laravel gives you two ways to send an email, and interviewers ask when each fits. - A **Mailable** (`Illuminate\Mail\Mailable`) *is* an email. It owns its envelope, its Blade or Markdown view and its attachments, and you send it with `Mail::to($address)->send(...)`. - A **notification** (`Illuminate\Notifications\Notification`) is an *event for a person*. Email is only one of its representations; the same class may also produce an SMS, a database row for an in-app bell and a broadcast. Its email representation is built in `toMail()`, usually as a `MailMessage`. ## Side by side | Aspect | Notification + `MailMessage` | Mailable | |---|---|---| | Channels | any set chosen by `via()` per recipient | email only | | Recipient | from the notifiable's mail route (`email` attribute or `routeNotificationForMail()`) | passed to `Mail::to()` or set in the envelope | | Layout | Laravel's notification template: greeting, lines, one button, salutation | any view or Markdown template you write | | Effort | a few chained calls | a class plus a template | | Best for | short transactional messages tied to an event | designed, content-heavy or email-specific messages | ## What MailMessage gives you `MailMessage` is intentionally small: - `subject()` — when you omit it, the mail channel derives one from the class name, so `AppointmentReminder` becomes "Appointment Reminder". - `greeting()`, `line()`, `lineIf()`, `lines()`, `salutation()` — the text. Lines added before `action()` render above the button; lines added after it render below. - `action($text, $url)` — the call-to-action button. There is exactly one: a second `action()` call replaces the first. - `error()` and `success()` — set the message level; `error()` turns the button red, for messages like a failed payment. - `from()`, `replyTo()`, `cc()`, `bcc()`, `attach()`, `attachData()`, `tag()`, `metadata()`, `mailer()` — sender, extras and mailer choice. - `markdown($view, $data)`, `view($view, $data)`, `text($view, $data)` — escape hatches to your own template. To restyle every notification email at once, `php artisan vendor:publish --tag=laravel-notifications` copies the `email.blade.php` template to `resources/views/vendor/notifications`. ## Beyond lines and a button `MailMessage` covers more of a real transactional email than candidates expect: - **Attachments:** `attach($path)`, `attachData($bytes, 'confirmation.ics')` and `attachFromStorage($path)` add files — an `.ics` calendar invite for the appointment, say. - **Mailer choice:** `mailer('postmark')` sends this notification's email through a specific configured mailer instead of the default. - **Provider extras:** `tag('appointment-reminder')` and `metadata('appointment_id', $id)` travel to providers that support them. - **Markdown notifications:** `php artisan make:notification AppointmentReminder --markdown=mail.appointments.reminder` generates a template and wires `toMail()` to `(new MailMessage)->markdown(...)`, so you write the body yourself while keeping the notification's channels. Each of these narrows the gap with a Mailable. The remaining reasons to reach for a Mailable are a genuinely custom layout, reuse of the same email outside notifications, or sending it on its own with `Mail::to()`. ## Returning a Mailable from toMail() When the email needs a real design but the event still belongs in a notification — the reminder should also go out by SMS and land in the bell — return a Mailable: 1. Build the mailable with the data it needs: `new AppointmentReminderMail($this->appointment)`. 2. Address it yourself: `->to($notifiable->email)`. The mail channel sends a returned Mailable as-is and does not add the notifiable's route. 3. For on-demand recipients, `$notifiable` is an `AnonymousNotifiable`; read the address with `$notifiable->routeNotificationFor('mail')`. You keep `via()`, queueing and the other channels in one class, and the email gets a fully custom layout. ## Choosing in practice - A reminder, a status change or an alert that may reach several channels: **notification with `MailMessage`**. - The same, but marketing insists on a branded layout: **notification whose `toMail()` returns a Mailable**. - A newsletter, a campaign or an order receipt with a PDF that is only ever email: **Mailable**. - Mail to an arbitrary address with no model: either works — `Mail::to('[email protected]')` or an on-demand notification. ## Pitfalls - Forgetting `->to()` on a returned Mailable: the mail channel does not address it for you, so the recipient you expected is never set. - Expecting several buttons from `MailMessage`: only the last `action()` survives. - Unset `APP_NAME`: the notification template prints the application name in its header and salutation, so "Laravel" appears in patients' inboxes. ## Summary - Notification = event with channels; Mailable = one email. - `MailMessage` is a fast, fixed layout with one action and a default subject from the class name. - `toMail()` may return a Mailable when the email needs its own design — address it yourself.

  • What subject does a notification email get if toMail() never calls subject()?
    The mail channel derives it from the notification's class name: the short class name is split into words and title-cased, so `AppointmentReminder` produces "Appointment Reminder". Set `subject()` whenever that is not what patients should read.
  • How do you change the layout of every notification email at once?
    Run `php artisan vendor:publish --tag=laravel-notifications`, which copies the notification `email.blade.php` template to `resources/views/vendor/notifications`, and edit it there. Individual notifications can still opt out with `markdown()` or `view()` on their `MailMessage`.

A MailMessage is the clinic's pre-printed reminder card: you fill in a greeting, a few lines and one 'confirm' box, and every card looks the same. A Mailable is a letter the designer lays out from scratch. When the reminder system hands the designer's letter to the post room, someone still has to write the patient's address on the envelope.

saying these in an interview costs you the question

  • A notification's email can only be built with MailMessage, never a Mailable.
  • MailMessage renders one button per action() call.
  • When toMail() returns a Mailable, the mail channel addresses it automatically.
  • Notifications are simply mailables with a different base class.
  • A MailMessage without subject() is sent with an empty subject line.