Skip to content

Laravel Mail — Mailable Classes and Email Sending

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about Laravel Mail. We cover key concepts, practical examples, and best practices to help you master this topic.

Laravel mail provides a clean API for building and sending emails using Mailable classes with Markdown templates, attachments, queuing, and multiple mailer drivers.

What You'll Learn

By the end of this tutorial, you'll create Mailable classes, design Markdown emails, attach files, queue mail delivery, configure mailers, and test mail sending.

Why Laravel Mail Matters

Mailable classes encapsulate email logic in reusable objects. Markdown mail provides responsive email templates without manual HTML, and queuing ensures fast responses.

Real-World Use

An invoice application creates Mailable InvoiceEmail with PDF attachment, sends via SMTP in production and log in development, and queues sending to avoid blocking the request.

Mail Path

flowchart LR
  A[Laravel Framework] --> B[Mail]
  B --> C[Mailable]
  B --> D[Markdown]
  B --> E[Attachments]
  B --> F[Mailers]
  B --> G{You Are Here}
  style G fill:#f90,color:#fff

Creating Mailables

Generate and build a Mailable class.

php artisan make:mail InvoiceEmail --markdown=emails.invoice
<?php
namespace App\Mail;
use App\Models\Invoice;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Attachment;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class InvoiceEmail extends Mailable {
    use Queueable, SerializesModels;
    public function __construct(
        public Invoice $invoice,
    ) {}
    public function envelope(): Envelope {
        return new Envelope(
            subject: "Invoice #{$this->invoice->id}",
            cc: ["billing@example.com"],
            bcc: ["archive@example.com"],
        );
    }
    public function content(): Content {
        return new Content(
            markdown: "emails.invoice",
            with: [
                "total" => number_format($this->invoice->total, 2),
                "dueDate" => $this->invoice->due_date->format("F j, Y"),
            ],
        );
    }
    public function attachments(): array {
        $pdf = PDF::loadView("pdfs.invoice", ["invoice" => $this->invoice]);
        return [
            Attachment::fromData(fn() => $pdf->output(), "invoice-{$this->invoice->id}.pdf")
                ->withMime("application/pdf"),
        ];
    }
}

Markdown Email Template

Design beautiful emails with Markdown.

{{-- resources/views/emails/invoice.blade.php --}}
<x-mail::message>
# Invoice #{{ $invoice->id }}

Dear {{ $invoice->user->name }},

Your invoice for **${{ $total }}** is due on **{{ $dueDate }}**.

<x-mail::button :url="url('/invoices/' . $invoice->id)">
View Invoice
</x-mail::button>

<x-mail::table>
| Item | Quantity | Price |
| :--- | :---: | ---: |
@foreach ($invoice->items as $item)
| {{ $item->description }} | {{ $item->quantity }} | ${{ number_format($item->total, 2) }} |
@endforeach
</x-mail::table>

Thanks,<br>
{{ config('app.name') }}
</x-mail::message>

Sending Mail

Send mail through different mailers.

<?php
use App\Mail\InvoiceEmail;
use Illuminate\Support\Facades\Mail;
// Send via default mailer
Mail::to($user)->send(new InvoiceEmail($invoice));
// Queue for sending
Mail::to($user)->queue(new InvoiceEmail($invoice));
// Later delivery
Mail::to($user)->later(now()->addHour(), new InvoiceEmail($invoice));
// Specific mailer
Mail::mailer("ses")->to($user)->send(new InvoiceEmail($invoice));
// Multiple recipients
Mail::to($user)->cc($manager)->bcc($archive)->queue(new InvoiceEmail($invoice));

Mailer Configuration

Configure multiple mail drivers.

<?php
// config/mail.php
"mailers" => [
    "smtp" => [
        "transport" => "smtp",
        "host" => env("MAIL_HOST", "smtp.mailgun.org"),
        "port" => env("MAIL_PORT", 587),
        "encryption" => env("MAIL_ENCRYPTION", "tls"),
        "username" => env("MAIL_USERNAME"),
        "password" => env("MAIL_PASSWORD"),
    ],
    "ses" => [
        "transport" => "ses",
    ],
    "mailgun" => [
        "transport" => "mailgun",
    ],
    "log" => [
        "transport" => "log",
        "channel" => env("MAIL_LOG_CHANNEL"),
    ],
],

Testing Mail

Test mail sending in tests.

<?php
namespace Tests\Feature\Mail;
use Tests\TestCase;
use App\Mail\InvoiceEmail;
use Illuminate\Support\Facades\Mail;
class InvoiceEmailTest extends TestCase {
    public function test_invoice_email_sends(): void {
        Mail::fake();
        Mail::assertNothingSent();
        $invoice = Invoice::factory()->create();
        Mail::to($invoice->user)->send(new InvoiceEmail($invoice));
        Mail::assertSent(InvoiceEmail::class, function ($mail) use ($invoice) {
            return $mail->invoice->id === $invoice->id;
        });
    }
    public function test_invoice_email_content(): void {
        $invoice = Invoice::factory()->create();
        $mailable = new InvoiceEmail($invoice);
        $mailable->assertHasSubject("Invoice #{$invoice->id}");
        $mailable->assertHasCc("billing@example.com");
        $mailable->assertSeeInHtml("View Invoice");
        $mailable->assertSeeInText("$" . number_format($invoice->total, 2));
    }
}

Common Mistakes

1. Sending Mail Synchronously

Mail sending takes 100-500ms. Always queue mail sending to keep responses fast.

2. Not Handling Email Failures

Failed mail silently fails. Use failover mailer or catch exceptions.

3. Forgetting to Use Markdown

Markdown mail generates responsive HTML automatically. Manual HTML mail breaks on mobile.

4. Attachment Memory Issues

Large attachments in memory cause OOM. Use Attachment::fromStorage() for disk files.

5. Not Configuring Log Mailer for Development

Without log mailer, development emails go nowhere. Use log driver to inspect sent mail.

Practice Questions

1. How do you queue mail sending?

Use Mail::to($user)->queue(new Mailable()) instead of send().

2. What is Markdown mail?

A Mailable that renders a Markdown view into responsive HTML email.

3. How do you add an attachment?

Add Attachment::fromPath($path) or Attachment::fromData() to the attachments() method.

4. What is the log mailer driver?

Writes email content to the log instead of sending, useful for development.

5. Challenge: Create a Mailable with PDF attachment and queue.

<?php
class ReceiptMail extends Mailable {
    use Queueable;
    public function attachments(): array {
        return [Attachment::fromStorage("receipts/receipt-{$this->order->id}.pdf")];
    }
    public function envelope(): Envelope {
        return new Envelope(subject: "Receipt for Order #{$this->order->id}");
    }
    public function content(): Content {
        return new Content(markdown: "emails.receipt");
    }
}

FAQ

What is the difference between Mail::send() and Mail::queue()?

send() sends immediately. queue() sends asynchronously via the queue worker.

Can I use Blade components in Markdown mail?

Yes. Mail components use Tailwind CSS under the hood.

How do I configure multiple mailers?

Define multiple mailers in config/mail.php and set MAIL_MAILER.

What mail driver should I use for development?

Use the log driver. It writes emails to storage/logs/laravel.log.

Can I preview mail without sending?

Yes. Return the Mailable as a response or use Mail::pretend().

Mini Project: Invoice Mail Automation

Build a complete invoice mailing system.

<?php
class InvoiceMailer {
    public function sendInvoice(Invoice $invoice): void {
        Mail::to($invoice->user)
            ->cc($invoice->user->manager)
            ->queue(new InvoiceEmail($invoice));
    }
    public function sendReminder(Invoice $invoice): void {
        Mail::mailer("ses")
            ->to($invoice->user)
            ->queue(new PaymentReminder($invoice));
    }
}

What's Next

Laravel Notifications Laravel Queues Deep Laravel Cache Deep

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro