Free SKILL.md scraped from GitHub. Clone the repo or copy the file directly into your Claude Code skills directory.
npx versuz@latest install event4u-app-agent-config-agent-src-uncompressed-skills-laravel-mailgit clone https://github.com/event4u-app/agent-config.gitcp agent-config/SKILL.MD ~/.claude/skills/event4u-app-agent-config-agent-src-uncompressed-skills-laravel-mail/SKILL.md---
name: laravel-mail
description: "Use when building Laravel emails — Mailables, Markdown templates, queued sending, attachments, previews — even when the user says 'send this as an email' without naming Mailables."
source: package
domain: engineering
---
# laravel-mail
## When to use
Use this skill when building email functionality:
- Mailable classes with HTML/Blade or Markdown templates
- Queued email sending
- Attachments and inline images
- Mail testing and previewing
For **simple notification emails** (one-off messages), see [laravel-notifications](../laravel-notifications/SKILL.md).
Use Mailables when you need full control over the email template.
## Procedure: Create a Mailable
1. **Generate class** — `php artisan make:mail InvoiceMail --markdown=emails.invoice`.
2. **Configure** — Set subject, from, attachments, queuing (`ShouldQueue`).
3. **Create template** — Markdown template in `resources/views/emails/`.
4. **Verify** — Send test email, confirm rendering and delivery.
### Example
```bash
php artisan make:mail InvoiceMail --markdown=emails.invoice
```
```php
declare(strict_types=1);
namespace App\Mail;
use App\Models\Invoice;
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 InvoiceMail extends Mailable implements ShouldQueue
{
use Queueable;
use SerializesModels;
public function __construct(
private readonly Invoice $invoice,
) {}
public function envelope(): Envelope
{
return new Envelope(
subject: 'Invoice #' . $this->invoice->getNumber(),
replyTo: ['billing@example.com'],
);
}
public function content(): Content
{
return new Content(
markdown: 'emails.invoice',
with: [
'invoice' => $this->invoice,
'url' => route('invoices.show', $this->invoice->getId()),
],
);
}
/** @return array<int, \Illuminate\Mail\Mailables\Attachment> */
public function attachments(): array
{
return [
Attachment::fromPath('/path/to/invoice.pdf')
->as('invoice-' . $this->invoice->getNumber() . '.pdf')
->withMime('application/pdf'),
];
}
}
```
## Markdown templates
```blade
{{-- resources/views/emails/invoice.blade.php --}}
<x-mail::message>
# Invoice {{ $invoice->getNumber() }}
Thank you for your order. Here is your invoice summary:
<x-mail::table>
| Item | Amount |
|:-----|-------:|
@foreach ($invoice->getItems() as $item)
| {{ $item->getName() }} | {{ $item->getFormattedAmount() }} |
@endforeach
| **Total** | **{{ $invoice->getFormattedTotal() }}** |
</x-mail::table>
<x-mail::button :url="$url">
View Invoice
</x-mail::button>
Thanks,<br>
{{ config('app.name') }}
</x-mail::message>
```
## Sending mail
```php
// Send immediately
Mail::to($user)->send(new InvoiceMail($invoice));
// Queue for background sending (preferred)
Mail::to($user)->queue(new InvoiceMail($invoice));
// Send later
Mail::to($user)->later(now()->addMinutes(10), new InvoiceMail($invoice));
// Multiple recipients
Mail::to($users)
->cc($manager)
->bcc('archive@example.com')
->send(new InvoiceMail($invoice));
```
## Testing
```php
// Assert mail was sent
Mail::fake();
// ... trigger action ...
Mail::assertSent(InvoiceMail::class, function (InvoiceMail $mail) use ($user) {
return $mail->hasTo($user->getEmail());
});
Mail::assertNotSent(InvoiceMail::class);
Mail::assertNothingSent();
Mail::assertQueued(InvoiceMail::class);
```
## Previewing in browser
```php
// routes/web.php (local only)
Route::get('/mail-preview', function () {
$invoice = Invoice::factory()->create();
return new InvoiceMail($invoice);
});
```
## Core rules
- **Always queue emails** — implement `ShouldQueue` to avoid blocking requests.
- **Use Markdown templates** for consistent styling across email clients.
- **Use Envelope + Content** pattern (Laravel 11+) — not the old `build()` method.
- **Test with `Mail::fake()`** — verify recipients, content, and queuing.
- **Keep Mailables focused** — one Mailable per email type.
## Output format
1. Mailable class with envelope, content, and attachments
2. Blade/Markdown email template
3. Queued mail dispatch integration
## Auto-trigger keywords
- Mailable
- email template
- send mail
- Mail::to
- markdown email
- mail attachment
## Gotcha
- Always queue emails (`ShouldQueue`) — synchronous sending blocks the request.
- The model forgets that mail templates are Blade files — they need to be published/created.
- Don't test email content with `Mail::fake()` alone — it doesn't render the template. Use `Mail::assertSent()` with closure.
## Do NOT
- Do NOT send emails synchronously in request lifecycle — always queue.
- Do NOT use `build()` method — use `envelope()`, `content()`, `attachments()`.
- Do NOT hardcode email addresses — use config or environment variables.
- Do NOT put HTML in Mailable classes — use Blade templates.