Laravel Mail — Mailable Classes and Email Sending
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
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