Guides

Send email in Laravel

How to send transactional email from Laravel using mailables, queued mail, Markdown templates, and a proper email API instead of the server mail() function.

Published Last updated
ArawaMail branded graphic for sending email in Laravel

Laravel already knows how to send mail. The mistake is leaving it on the default log or sendmail driver once real users exist. Password resets, invoices, and invitations should go through mailables, a queue, and a provider that authenticates your domain.

This guide is the product-level version. For package install and a first request, use the ArawaMail Laravel quickstart.

Prefer mailables over raw Mail::raw

A mailable is a class you can test, queue, and preview. Put one class per message type: OrderReceipt, ResetPassword, WorkspaceInvite. Keep the subject and the data the view needs on the class, not scattered through controllers. For one-off plain-text diagnostics, see when to use Laravel Mail::raw.

Use Markdown mail when the message is mostly text and a button. Laravel will produce HTML and a plain-text part. That combination is easier on filters than a giant table-based template you never proofread.

Create a queued Markdown mailable

Generate the class and its Markdown view:

php artisan make:mail OrderReceipt --markdown=mail.orders.receipt

Then give the mailable an explicit envelope and content definition. Implementing ShouldQueue makes queued delivery the default whenever the mailable is sent.

<?php

namespace App\Mail;

use App\Models\Order;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class OrderReceipt extends Mailable implements ShouldQueue
{
    use Queueable, SerializesModels;

    public function __construct(public Order $order) {}

    public function envelope(): Envelope
    {
        return new Envelope(
            subject: "Receipt for order {$this->order->number}",
        );
    }

    public function content(): Content
    {
        return new Content(
            markdown: 'mail.orders.receipt',
        );
    }
}

Use the generated Markdown view for the copy and button:

@component('mail::message')
# Payment received

We received payment for order {{ $order->number }}.

@component('mail::button', ['url' => route('orders.show', $order)])
View receipt
@endcomponent

Thanks,
{{ config('app.name') }}
@endcomponent

Queue it after the order has been committed:

use App\Mail\OrderReceipt;
use Illuminate\Support\Facades\Mail;

Mail::to($order->customer_email)
    ->queue(new OrderReceipt($order));

If the send is dispatched inside a database transaction, use an after-commit queue configuration or dispatch only after the transaction succeeds. Otherwise a fast worker can try to render an order that has not committed yet.

Always queue it

Implement ShouldQueue on anything that talks to the network. A signup request should write the user and return. The mail worker should talk to the email API. If the provider is slow, the user should not wait on that latency, and a retry should not create a second account.

Give the job a reasonable timeout and make the send idempotent if you can: the same receipt should not go out three times because the worker crashed after the API accepted the message.

SMTP vs the email API

SMTP is familiar and works with Laravel’s built-in transport. An HTTP API gives you a message id, clearer errors, and fewer surprises through proxies and shared hosts that block port 25. For application mail, the API is usually the better default. Keep SMTP for devices and apps that cannot speak HTTP.

ArawaMail supports both the JSON API (including a Resend-compatible shape) and a Mailgun-compatible path, so existing Laravel packages often need a base URL and key rather than a rewrite.

What to configure in production

  • From name and address on a domain you control, not example@localhost.
  • Queue connection that actually runs in production (Redis, database, or SQS), not sync.
  • Failed job handling so a bad address is visible.
  • A single place to read delivered, bounced, and deferred events. Webhooks belong here, not in ad-hoc logs.

Content that Laravel developers forget

Set a reply-to that a human reads if the mail is an invite. Do not attach a 4MB logo. Do not put the reset token only in an image. If you localize subjects, keep them honest: “Reset your password” beats “Action required on your account!!!”.

Laravel mailable FAQ

What is a Laravel mailable?

A Laravel mailable is a PHP class that describes one email: its subject, recipients, view or Markdown content, attachments, and headers. It keeps message behavior out of controllers and gives tests a stable class to inspect.

How do I create a mailable in Laravel?

Run php artisan make:mail NameOfMessage. Add --markdown=view.name when Laravel should generate a Markdown mail view as well as the class.

Should a Laravel mailable implement ShouldQueue?

Yes for most production transactional email. Implement ShouldQueue, run a real queue worker, configure retries and failed jobs, and avoid the sync queue connection in production.

What is the difference between Mail::raw and a mailable?

Mail::raw() is convenient for a small plain-text message. A mailable is the better choice when the message needs reusable content, Markdown or HTML, attachments, localization, testing, or queued delivery.

How do I test a Laravel mailable without sending it?

Render or preview the mailable locally, and use Laravel’s Mail::fake() with assertions in automated tests. Test the subject and important view data separately from the provider integration.

When you are ready to send from a real domain, create an API key in ArawaMail and point Laravel at it. The first 3,000 transactional emails each month are free. Then read what transactional email is if you need to explain the difference to the rest of the team.

Simple, transparent plans

Start free. Grow when your email does.

Get one domain, API access and 3,000 transactional emails every month at no cost.

Compare plans