# Sending Emails in Laravel with Mailables and Templates

> Setting up reliable, maintainable transactional emails using Laravel's Mailable class and Blade email templates.

- Published: 2017-03-05
- Category: Web Development
- Tags: Laravel
- Reading time: 4 min read
- Source: https://www.muhammetsafak.com.tr/en/blog/sending-emails-in-laravel-with-mailables-and-templates/
- Language: en-US
- Author: Muhammet Şafak

---
Transactional email — the "you've registered", "your order has been received", "reset your password" kind — is something every web application eventually needs. But what starts as "just send an email" can quickly turn into an unmaintainable heap of `mail()` calls a few weeks later.

The **Mailable** classes introduced in Laravel 5.3 go a long way toward taming that mess. Moving email-sending logic into a dedicated class makes testing easier and keeps templates organized.

## Creating a Mailable class

An Artisan command gets you started quickly:

```bash
php artisan make:mail OrderConfirmed
```

This creates `app/Mail/OrderConfirmed.php`. The basic structure looks like this:

```php
namespace App\Mail;

use App\Order;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class OrderConfirmed extends Mailable
{
    use SerializesModels;

    public $order;

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

    public function build()
    {
        return $this->subject('Your Order Has Been Received')
                    ->view('emails.orders.confirmed');
    }
}
```

Declaring `$order` as public makes it automatically available in the template — no extra `with()` call needed. I like this small convention; having the data visible right on the class makes the flow much easier to follow than burying it inside method calls.

The `SerializesModels` trait kicks in for queued emails: instead of serializing the entire model, it stores only the ID in the queue and re-fetches the model at send time. Using this trait early on saved me from the subtle bugs that come with carrying large model objects through the queue.

## Blade email template

The `resources/views/emails/orders/confirmed.blade.php` template is written just like any other [Blade template](/en/blog/blade-template-engine-eliminating-repetitive-html):

```blade
<!DOCTYPE html>
<html>
<body>
    <h1>Your Order Has Been Received</h1>
    <p>Dear {{ $order->user->name }},</p>
    <p>
        Your order #{{ $order->id }} has been received.
        Total amount: {{ number_format($order->total, 2) }}
    </p>
    <p>You can track your order details from your account.</p>
</body>
</html>
```

You can use `@extends` and `@section` in email templates just as you would elsewhere, so it is straightforward to create a shared email layout and reuse it across all messages. Keeping the header, footer, and brand colors in one place is a real convenience.

One detail worth noting: email clients — particularly older versions of Outlook — tend to ignore external CSS files. That means styles in email templates should be written inline (`style="..."`), or converted to inline styles at build time with a tool like `premailer`. Markdown Mailable partially solves this; its built-in templates already use inline styles.

## Sending

Sending from a controller is a single line:

```php
Mail::to($order->user)->send(new OrderConfirmed($order));
```

To send via the queue instead:

```php
Mail::to($order->user)->queue(new OrderConfirmed($order));
```

Queuing delivery matters for responsiveness — when the mail server is slow to respond, you don't want to hold up the HTTP request. With SMTP in particular, a server connection can stretch to several seconds, so queuing becomes nearly mandatory if you don't want users to feel that lag.

Beyond `Mail::to()`, you can chain `cc()` and `bcc()` without needing to look for a separate method.

## Configuration and environment differences

You can select the driver from the `.env` file: `smtp`, `mailgun`, `sendmail`, `log`… In development I use the `log` driver; emails are written to `storage/logs/laravel.log` and nothing is actually sent. In production, SMTP configuration takes over.

```
MAIL_DRIVER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=...
MAIL_PASSWORD=...
MAIL_FROM_ADDRESS=noreply@example.com
MAIL_FROM_NAME="Application Name"
```

Adding a testing tool like Mailtrap early in the development process prevents accidentally emailing real users. I learned this the hard way: real SMTP credentials left in a local environment caused order confirmation emails to be sent to actual users during a test run. Since then, the `log` driver has been a standard part of my development environment.

## Markdown Mailable

The Markdown Mailable feature introduced in Laravel 5.4 generates both HTML and plain-text versions simultaneously. It comes with built-in components like `mail::message` and `mail::button`. When a custom design is not required, these templates offer a clean, fast starting point.

```php
return $this->subject('Your Order Has Been Received')
            ->markdown('emails.orders.confirmed-markdown');
```

There is another advantage to Markdown Mailable: the `vendor:publish` command lets you copy the built-in templates into your project, so you can customize just the HTML/CSS to match your brand. Adapting an existing foundation rather than writing all templates from scratch saves a significant amount of time.

## Managing multiple email types

As a project grows, so does the variety of emails: registration confirmation, password reset, order notification, invoice… When each one has its own Mailable class and its own Blade template, the `app/Mail/` directory fills up quickly. That might look like clutter, but being able to change each email type independently is a real advantage. Having a dedicated class per type — rather than a single `sendEmail($type, $data)` function — means you can update a template or subject line for one email without touching any of the others.

Structuring email delivery around Mailable classes genuinely pays off as a project grows. Instead of scattered `mail()` calls, every message type has a home, and tracking what gets sent, when, and to whom becomes far easier.
