Skip to content

Laravel - Sending Email

Laravel provides a clean, simple API over the popular Symfony Mailer component (formerly SwiftMailer), allowing you to quickly get started sending mail through a local or cloud-based service of your choice.

Laravel’s email services are configured via your application’s .env file and the config/mail.php configuration file. The .env file allows you to quickly change mail drivers and credentials without modifying code.

Common .env mail variables:

MAIL_MAILER=smtp
MAIL_HOST=mailpit
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"

Explanation of common options:

  • MAIL_MAILER: The driver to use (e.g., smtp, ses, mailgun, postmark, sendmail, log). For local development, log (writes emails to log files) or a tool like Mailpit/MailHog (smtp configured to its host/port) is highly recommended.
  • MAIL_HOST, MAIL_PORT, MAIL_USERNAME, MAIL_PASSWORD, MAIL_ENCRYPTION: Credentials for SMTP.
  • MAIL_FROM_ADDRESS, MAIL_FROM_NAME: The default sender address and name for your emails.

For services like Gmail, you might need to generate an “App Password” if 2-Step Verification is enabled. Example for Gmail SMTP:

MAIL_MAILER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=your-gmail-username@gmail.com
MAIL_PASSWORD=your-gmail-app-password
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS="your-gmail-username@gmail.com"
MAIL_FROM_NAME="Your Application Name"

After modifying your .env file, clear the config cache: php artisan config:clear.

In Laravel, each type of email sent by your application is represented as a “mailable” class. These classes are stored in the app/Mail directory. You can generate a new mailable class using the Artisan command:

php artisan make:mail OrderShipped

This command will create app/Mail/OrderShipped.php. A mailable class contains methods for building the email, such as defining its sender, recipients, subject, and the view to use.

Within a mailable class, you typically configure its properties (data to pass to the view) in the constructor and build the email message within the build method (older versions) or envelope and content methods (newer versions).

Example Mailable (OrderShipped.php - modern syntax with Envelope and Content):

<?php
namespace App\Mail;
use App\Models\Order; // Assuming you have an Order model
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class OrderShipped extends Mailable implements ShouldQueue // Optional: for queueing
{
use Queueable, SerializesModels;
public Order $order;
/**
* Create a new message instance.
*/
public function __construct(Order $order)
{
$this->order = $order;
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
return new Envelope(
from: new \Illuminate\Mail\Mailables\Address('noreply@example.com', 'Example App'),
subject: 'Your Order Has Shipped!',
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
view: 'emails.orders.shipped', // Blade view for the email
// with: ['orderName' => $this->order->name], // Alternative to public properties
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [
// Attachment::fromPath(public_path('/path/to/file.pdf')),
];
}
}

Creating the Email View (resources/views/emails/orders/shipped.blade.php):

Section titled “Creating the Email View (resources/views/emails/orders/shipped.blade.php):”
<!DOCTYPE html>
<html>
<head>
<title>Order Shipped</title>
</head>
<body>
<h1>Hi, {{ $order->customer_name }}!</h1> {{-- Accessing public property from Mailable --}}
<p>Your order #{{ $order->id }} has been shipped.</p>
<p>Thank you for your purchase!</p>
</body>
</html>

To send a mailable, use the to method on the Mail facade. The to method accepts an email address, a user instance, or a collection of users. Then, call the send method with an instance of your mailable class.

Example in a Controller:

<?php
namespace App\Http\Controllers;
use App\Mail\OrderShipped;
use App\Models\Order; // Assuming Order model exists
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
class OrderController extends Controller
{
public function ship(Request $request, string $orderId)
{
$order = Order::findOrFail($orderId); // Fetch the order
// Send the email
Mail::to($order->customer_email) // Or $request->user() if sending to the logged-in user
// ->cc($moreUsers)
// ->bcc($evenMoreUsers)
->send(new OrderShipped($order));
return redirect()->route('orders.show', $orderId)->with('status', 'Order shipped email sent!');
}
}

You would typically call this ship method from a route definition.

Sending Plain Text Emails (using Mail::raw or text view in Mailable)

Section titled “Sending Plain Text Emails (using Mail::raw or text view in Mailable)”

If you need to send a simple plain text email without a Blade template, you can use Mail::raw():

Mail::raw('This is a plain text email body.', function ($message) {
$message->to('recipient@example.com')
->subject('Simple Text Email');
});

For Mailables, you can specify a text view in the Content object:

public function content(): Content
{
return new Content(
view: 'emails.orders.shipped', // HTML view
text: 'emails.orders.shipped_plain' // Plain text view
);
}

To add attachments, use the attach method within the Mailable’s attachments method or directly on the Envelope (or build method in older versions).

// In Mailable's attachments() method (newer Laravel):
use Illuminate\Mail\Mailables\Attachment;
public function attachments(): array
{
return [
Attachment::fromPath(public_path('documents/invoice.pdf'))
->as('customer-invoice.pdf')
->withMime('application/pdf'),
Attachment::fromStorageDisk('s3', 'avatars/profile.jpg'), // Attach from a storage disk
];
}

Sending emails can significantly slow down your application’s response time. Laravel allows you to easily queue emails for background sending. Implement the ShouldQueue interface on your mailable class.

class OrderShipped extends Mailable implements ShouldQueue
{ ... }

Then, when you call Mail::send(), Laravel will automatically push the job to your queue. You’ll need to have a queue worker running: php artisan queue:work.

During local development, you often don’t want to send real emails. Tools like Mailpit or MailHog act as local SMTP servers that catch all outgoing emails and display them in a web interface. This is invaluable for testing.

Configure your .env to point to Mailpit (usually running on localhost:1025):

MAIL_MAILER=smtp
MAIL_HOST=localhost # or 127.0.0.1
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null # or 'tls' if Mailpit is configured for it

Common Learning Obstacle: Email Configuration Issues

Many beginners struggle with email sending due to incorrect .env configuration or issues with their mail server (e.g., Gmail blocking less secure apps, ISP blocking SMTP ports). Using MAIL_MAILER=log or Mailpit during development can help isolate whether the issue is with Laravel’s code or the mail server setup.

For comprehensive details, refer to the official Laravel documentation on Mail: https://laravel.com/docs/mail