Muhammad Yasir Amin

Loading websiteโ€ฆ

More from Muhammad Yasir Amin

Build Along With Me

Practical walkthroughs on Laravel, React, APIs and deployment workflows.

Subscribe on YouTube

Laravel 13 Send Email with Attachment: Mailable Attachment Examples

๐Ÿ› ๏ธ Laravel Tutorials4 min read

Laravel 13 Send Email with Attachment: Mailable Attachment Examples

Muhammad Yasir Amin

Muhammad Yasir Amin

Senior Full Stack Engineerโ€ขNov 10, 2026
Laravel 13 Send Email with Attachment: Mailable Attachment Examples

To send an email with an attachment in Laravel 13, return an array of Attachment objects from the attachments() method of your Mailable. Use Attachment::fromPath() for a file on disk, Attachment::fromStorage() for a file on a filesystem disk, and Attachment::fromData() for content generated in memory such as a PDF.

This tutorial targets Laravel 13 (PHP 8.3 to 8.5). The code was written for this article and not executed in a live project, so try it with the log mailer first.

What do you need before you start?

  • A Laravel 13 application on PHP 8.3 or newer.
  • Mail settings in .env. For local testing, MAIL_MAILER=log writes the full raw email, including the base64 attachment, to storage/logs/laravel.log. A tool like Mailpit shows attachments in a real inbox UI.
  • A file to attach, for example storage/app/private/reports/invoice.pdf.

Step 1: How do you create the Mailable?

Code
php artisan make:mail InvoiceMail

This creates app/Mail/InvoiceMail.php with envelope(), content() and attachments() methods.

Step 2: How do you attach files in attachments()?

Here is a Mailable that attaches a stored file and a file generated in memory.

JavaScript
namespace App\Mail;

use App\Models\Order;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Attachment;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;

class InvoiceMail extends Mailable
{
    public function __construct(public Order $order)
    {
    }

    public function envelope(): Envelope
    {
        return new Envelope(subject: 'Your invoice #' . $this->order->id);
    }

    public function content(): Content
    {
        return new Content(view: 'emails.invoice');
    }

    public function attachments(): array
    {
        return [
            Attachment::fromStorage('reports/invoice-' . $this->order->id . '.pdf')
                ->as('invoice.pdf')
                ->withMime('application/pdf'),

            Attachment::fromData(
                fn () => $this->order->toCsv(),
                'order.csv'
            )->withMime('text/csv'),
        ];
    }
}

toCsv() is a placeholder for your own method that returns a string. fromStorage() reads from the default disk; use fromStorageDisk('s3', 'path') for another disk. as() sets the filename the recipient sees and withMime() sets the content type.

How do you attach a file from a path?

Use Attachment::fromPath(public_path('files/terms.pdf')) when the file lives outside a configured disk. Always build the path yourself; never concatenate user input into it, or an attacker could request any file the server can read.

Step 3: How do you create the email view?

Create resources/views/emails/invoice.blade.php.

HTML / XML
<p>Hello {{ $order->customer_name }},</p>
<p>Your invoice for order #{{ $order->id }} is attached.</p>

Public properties on the Mailable, like $order, are available in the view automatically.

Step 4: How do you send it?

Call it from a controller, command or job.

PHP
use App\Mail\InvoiceMail;
use App\Models\Order;
use Illuminate\Support\Facades\Mail;

Route::post('/orders/{order}/send-invoice', function (Order $order) {
    Mail::to($order->customer_email)->send(new InvoiceMail($order));

    return back()->with('status', 'Invoice sent.');
});

For slow attachments such as generated PDFs, implement ShouldQueue on the Mailable or call Mail::to(...)->queue(...) so the web request does not wait for the mail server.

Can you attach an uploaded file?

Yes. Pass the uploaded file to the Mailable and attach it with Attachment::fromPath($file->getRealPath()). For queued mail, store the upload first and attach it from storage, because a temporary upload path may not exist when the worker runs. Validate the upload type and size before sending.

JavaScript
$request->validate([
    'document' => ['required', 'file', 'mimes:pdf,docx', 'max:5120'],
]);

$path = $request->file('document')->store('mail-attachments');

Common mistakes

  • Wrong disk path. fromStorage() uses the default disk, which is local (storage/app/private) in new Laravel apps, not public.
  • Queued mail with temporary files. Temporary upload paths vanish before a worker picks up the job.
  • Huge attachments. Many providers reject messages over 20 to 25 MB. Send a signed download link for big files instead.
  • Using the old attach() style. Older tutorials call $this->attach() inside build(); the attachments() method is the current approach.
  • Missing MIME type for generated data. Without withMime(), some clients cannot open the file.

How do you verify it works?

  1. Set MAIL_MAILER=log and send the mail.
  2. Search storage/logs/laravel.log for Content-Disposition: attachment; filename=invoice.pdf.
  3. Or use Mailpit and confirm the attachment appears and opens.
  4. Write a test with Mail::fake() and call $mailable->assertHasAttachment(...) inside Mail::assertSent().
PHP
Mail::fake();

Mail::to('a@example.com')->send(new InvoiceMail($order));

Mail::assertSent(InvoiceMail::class, function ($mail) {
    return $mail->hasTo('a@example.com');
});

FAQ

Can I attach several files?

Yes. Return as many Attachment objects as you need in the array.

Does it work with notifications?

Yes. On a MailMessage, use ->attach(), ->attachFromStorage() or ->attachData().

How do I embed an inline image?

Use $message->embed($path) in a Blade mail view instead of an attachment.

References

Article Index & Topic Tags
LaravelMailLaravel-13Attachments

YouTube Channel

Build Along With Me

Practical walkthroughs on Laravel, React, APIs and deployment workflows.

Muhammad Yasir Amin
Available for Contracts
Author & Solutions Architect

Senior Full Stack Engineer

Independent Senior Full Stack Engineer working on Laravel, React and Next.js applications, APIs and delivery workflows.

Laravel & PHP ApplicationsReact & Next.js InterfacesREST APIs & IntegrationsDatabase Queries & DebuggingDocker Development WorkflowsClear Scope & Handover
Discuss Your Next Step

What Does Your Application Need Next?

Share your current system, the problem you want to solve and what a useful result looks like. We can identify the right starting point and agree on a practical scope.

Technical Audit

Find the Next Useful Fix

Application Development

Scoped, Reviewable Milestones

Ongoing Support

Agreed Capacity and Priorities

Share your goals, current stack and requirements before the call.