Queuing in Laravel can feel a bit magical… until it suddenly isn’t.
A lot of developers run into the same moment of confusion:
you add ShouldQueue to a class, hit “send email,” and then wonder:
“Wait… did this actually go into the queue, or did it still run instantly?”
The tricky part is that Laravel behaves differently depending on what kind of class you’re working with. Mailables and Jobs may look similar on the surface, but Laravel treats them in completely different ways behind the scenes.
Let’s walk through what really happens — in plain language.
Mailables vs Jobs: Two Different Worlds
In Laravel, there are two categories you’ll see involved in email:
Mailables
These are classes that represent the email itself — subject, view, data, etc.
Jobs
These are background tasks that can do anything: send emails, log data, hit APIs, resize images, queue more jobs… whatever you want.
Both can implement ShouldQueue, but only one behaves like people expect.
Mailables: The Emails That Queue Themselves
If you create a mailable like this:
class WelcomeMail extends Mailable implements ShouldQueue
{
//
}
And then send it:
Mail::to($user)->send(new WelcomeMail());
Laravel does something very convenient:
The email is pushed onto the queue automatically.
No special methods.
No dispatch calls.
No extra steps.
Laravel sees that the mailable implements ShouldQueue, and instead of sending it right away, it quietly hands it over to your queue worker.
So even though you called send(), Laravel does not actually send it yet — it queues it behind the scenes.
This is intentional. Mailables have their own pipeline inside the framework, and Laravel checks for ShouldQueue on every one of them.
Jobs: The Tasks That Don’t Queue Themselves
Now compare that to a job:
class SendWelcomeEmailJob implements ShouldQueue
{
public function handle()
{
Mail::to($this->user)->send(new WelcomeMail());
}
}
Even with ShouldQueue added, nothing happens automatically.
You must manually dispatch the job:
SendWelcomeEmailJob::dispatch($user);
If you don’t dispatch it, Laravel won’t run it — not now, not later, not ever.
The job simply sits there as an unused class.
Why the difference?
Jobs are general-purpose.
Laravel doesn’t assume how or when you want to run them.
ShouldQueue only tells Laravel how to run the job once it’s dispatched — not that it should dispatch it for you.
Mailables, on the other hand, live in a special system built specifically for sending email. So the framework steps in and helps.
Why Laravel Designed It This Way
Laravel’s philosophy here is simple:
Mailables should feel effortless. Jobs should stay flexible.
Mailables:
- Have one purpose — sending emails
- Often need to be queued for performance
- Should be easy to use
Jobs:
- Can be anything
- Might be dispatched in bulk, scheduled, chained, batched, or delayed
- Should be controlled by you, not automatic rules
That separation gives you both convenience and full control.
A Quick Comparison
| Feature | Mailable | Job |
|---|---|---|
Can implement ShouldQueue | Yes | Yes |
| Automatically pushed to queue | Yes | No |
Needs dispatch() | No | Yes |
| Designed for | Emails | Any background task |
| Who handles dispatching? | Laravel | You |
When Should You Use What?
Choose a queued Mailable when:
- You just need to send an email
- You want the simplest setup
- There’s no heavy logic involved
Choose a Job when:
- Your email requires extra steps before or after
- You’re doing more than just sending (logging, API calls, updating models, etc.)
- You need advanced queue features
Mailables keep your email flow clean.
Jobs let you build something more powerful.
The Short Version (The “Did It Queue?” Answer)
Emails only queue automatically when the class is a Mailable that implements ShouldQueue.
If it’s a Job, ShouldQueue simply tells Laravel how to run it — you still need to dispatch it yourself.
Understanding that difference removes 99% of the confusion around queued emails in Laravel.