How to Send Emails in Laravel: A Complete SMTP & API Guide

On March 19, 2024
19min read
Aleksandr Varnin Full Stack Developer @ Railsware
Ivan Djuric, an author at Mailtrap
Ivan Djuric Technical Content Writer @Mailtrap
Sending emails in laravel

In this article, I’ll show you how to send emails in Laravel step by step using Mailtrap, with a primary focus on deliverability.

As a Laravel developer, you might have heard about Mailtrap being mentioned as exclusively an email testing solution in the official Laravel documentation. But, this platform also offers Email Sending.

The processes and code snippets I’ll show you in this article are compatible with Laravel 9.x. and above.

Setting up Laravel email service before sending emails

Laravel has its own email services, which enable sending emails through local or cloud-based services.

For example, the Laravel and Symfony Mailer combination gives users a range of drivers they can use to send email via SMTP, third-party email-sending services, and the Sendmail MTA.

Although you could technically use sendmail to send emails from your web application, your emails are likely to be marked as spam. 

You could also send emails in Laravel without an external SMTP or API service by using the log driver, but it won’t actually send out your emails. Instead, it will write all email messages to your application’s files.

Other notable features of Laravel’s email services include:

  • Queueing emails. 
  • Creating regular plain text and HTML email messages.
  • Attaching files in different formats and MIME types, as well as raw data. 
  • Including inline attachments and embedding raw data into email templates. 
  • Previewing messages in-browser.
  • Markdown support (in quite a few frameworks) – create beautiful templates and easily include buttons, tables, or panels. 
  • Templating system – use various templates and configure views. 
  • Localization methods – set the desired language for a specific user.
  • Local development mailing – prevent sending test emails to real inboxes.

Generating and writing mailables

In Laravel, the config.mail.php file will be the main tool we’ll use for setting up email services. It has a mailers section, which has a sample configuration entry for each major mail drivers/transports supported by Laravel.

By default, Laravel picks which service to use based on how we set this file up. The setup allows us to choose different email services for different types of emails.

Additionally, Laravel uses a “mailable” class to represent different email types. These are stored in the app/Mail directory, but you won’t see it right away in your project. Instead, it will be generated when you create your first mailable class.

To create a mailable class, I used this Artisan command (included in Laravel):

php artisan make:mail MailableName

After creating a mailable class, I was able to see the contents and configure the class itself. For this, I used the methods from the following table:

MethodExplanation
EnvelopeReturns the Illuminate\Mail\Mailables\Envelope object, which defines the subject and the recipients.
ContentReturns the Illuminate\Mail\Mailables\Content object, which defines the Blade template used for generating message content.
AttachmentsReturns an array of attachments.

Sender configuration

Now, we need to specify the sender or the from email address and name. For this, we can either:

  • Specify the sender in the message Envelope object, like so:
use Illuminate\Mail\Mailables\Address;
use Illuminate\Mail\Mailables\Envelope;
 
/**
* Get the message envelope.
*
* @return \Illuminate\Mail\Mailables\Envelope
*/
public function envelope()
{
   return new Envelope(
       from: new Address('example@example.com', 'Test Sender'),
       subject: 'Test Email',
   );
}
  • Specify the sender in config/mail.php with a global from address, like so:
'from' => ['address' => 'example@example.com', 'name' => 'App Name']

Pro Tip: You should use the global from address if you plan to use it in all of the emails your application sends. I found this quite convenient as it prevented me from having to call from method in each of my mailable classes. On top of that, it served as my default from address as I didn’t specify any other.

Send email in Laravel using SMTP

Now that you have configured the Laravel email service, I’ll show you how to set up Mailtrap Email Sending as the SMTP service provider in your application/project, and then I’ll show you how I created mailable classes and ran tests.

Step 1. Use Mailtrap SMTP credentials for integration with your app

The first thing I did was insert my SMTP server credentials provided by Mailtrap Email Sending into the .env of my web application.

To do this, all you have to do is create a Mailtrap account, log in, and navigate to Sending Domains. There, you need to add and verify your domain, which only takes a few seconds.

If you’re a visual learner, Mailtrap has an easy-to-follow video for you.

Once you’ve verified your domain, Mailtrap will take you to the page from where you can copy the SMTP credentials and easily paste them into your project, app, or email-sending service.

Note: We’ll use the Transactional Stream for this chapter, and I’ll show you how you can use the Bulk Stream later on in the article.

And here’s what your SMTP mail credentials should look like once you integrate them into your Laravel code:

// .env file

MAIL_MAILER=smtp
MAIL_HOST=live.smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=//your username
MAIL_PASSWORD=// your password
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=from@example.com
MAIL_FROM_NAME=//your app name

Step 2. Verify your SMTP setup

Once I inserted the credentials and sent a test mail from my Laravel project, Mailtrap verified my SMTP setup.

Here’s what you should receive as a response if your test email was sent successfully:

  • “250 2.0.0 Ok: queued as …”

Then, simply click on the “Verify Setup,” which will start the verification process.

Step 3. Enable Mailtrap tracking settings (optional)

Now, this step is optional, but I highly recommend it as it allows me to monitor my email performance.

Namely, Mailtrap provides you with in-depth analytics through which you can keep an eye out on your opens, clicks, bounces, and more.

Here’s what I see when I open my stats:

And here’s the stats overview:

Step 4. Create mailable classes

Remember those mailable classes I told you about previously? It’s time to create them with the following command:

php artisan make:mail MyTestEmail

After running this command, you should see the MyTestEmail class under “app/mailMyTestEmail.php”.

Mytestemail class under app/mailMyTestEmail.php

Here’s the class code for your convenience:

<?php

namespace App\Mail;

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 MyTestEmail extends Mailable
{
    use Queueable, SerializesModels;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Get the message envelope.
     *
     * @return \Illuminate\Mail\Mailables\Envelope
     */

    public function envelope()
    {
        return new Envelope(
            subject: 'My Test Email',
        );
    }
 /**
     * Get the message content definition.
     *
     * @return \Illuminate\Mail\Mailables\Content
     */

    public function content()
    {
        return new Content(
            view: 'view.name',
        );
    }

    /**
     * Get the attachments for the message.
     *
     * @return array
     */
    public function attachments()
    {
        return [];
    }
}

See anything interesting? That’s right, the content() method returns a view. So, let’s go to resources/views and create a new folder with a blade.php file in it.

Ready to finally write some text? You can do so in the blade.php file, likewise:

Now, let’s return to the content() method and replace the name of the view it returned with the name of our newly created one.

You can also make things a bit more dynamic by making your email template/blade.php file include the recipient’s name using the with attribute to pass the name.

Check it out.

<?php

namespace App\Mail;

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 MyTestEmail extends Mailable
{
    use Queueable, SerializesModels;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct(private $name)
    {
        //
    }

    /**
     * Get the message envelope.
     *
     * @return \Illuminate\Mail\Mailables\Envelope
     */
    public function envelope()
    {
        return new Envelope(
            subject: 'My Test Email',
        );
    }

    /**
     * Get the message content definition.
     *
     * @return \Illuminate\Mail\Mailables\Content
     */
    public function content()
    {
        return new Content(
            view: 'mail.test-email',
            with: ['name' => $this->name],
        );
    }
}

To make this work, I had to make a small change in the test-email.blade.php view file as well. What this does is it allows it to accept the $name variable.

// resources/views/mail/test-email.blade.php

Hey {{$name}}, 
Can your Laravel app send emails yet? 😉 
Mailtrap

And lastly, let’s create a route in the routes/web.php file with this code:

<?php

use Illuminate\Support\Facades\Route;
use App\Mail\MyTestEmail;
use Illuminate\Support\Facades\Mail;

Route::get('/testroute', function() {
    $name = "Funny Coder";

    // The email sending is done using the to method on the Mail facade
    Mail::to('testreceiver@gmail.com'')->send(new MyTestEmail($name));
});

Here’s how the file should look then:

And, the moment of truth has come. I tested things out by running php artisan serve command and going to my browser, where I pasted the route I created. In my case, it was localhost:8000/testroute.

In case everything works, your email should land in the inbox of the “to” address you specified.

Emailing multiple recipients

To send emails to multiple people in Laravel, I used the following code:

foreach (['First Coder' => 'first-recipient@gmail.com', 'Second Coder' => 'second-recipient@gmail.com'] as $name => $recipient) {
    Mail::to($recipient)->send(new MyTestEmail($name));
}

What this code does is iterate over an array of recipients. It also re-creates the mailable instance each time, which is super useful as it prevents the sending of another email to every previous recipient at every iteration through the loop. Just imagine the headache. 🤕

“But how do I send emails to just one recipient while also cc-ing and bcc-ing a few others?”, you might be wondering. Simply follow this example in the MyTestEmail class:

return new Envelope(
    subject: 'My Test Email',
    cc: ['testreceiver-cc@gmail.com'],
    bcc: ['testreceiver-bcc@gmail.com']
);

Send email with attachments

To add attachments to my emails in Laravel, I simply modified the attachments method in the example code for sending emails I showed you earlier.

I first made them in the MyTestEmail class:

<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Queue\SerializesModels;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Mail\Mailables\Attachment;
use Illuminate\Contracts\Queue\ShouldQueue;

class MyTestEmail extends Mailable
{
    use Queueable, SerializesModels;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct(private $name, public $attachedFile){ }

    /**
     * Get the message envelope.
     *
     * @return \Illuminate\Mail\Mailables\Envelope
     */
    public function envelope()
    {
        return new Envelope(
            subject: 'My Test Email',
        );
    }

    /**
     * Get the message content definition.
     *
     * @return \Illuminate\Mail\Mailables\Content
     */
    public function content()
    {
        return new Content(
            view: 'mail.test-email',
            with: ['name' => $this->name],
        );
    }

    /**
     * Get the attachments for the message.
     *
     * @return array
     */
    public function attachments()
    {
        return [
            Attachment::fromPath($this->attachedFile),
        ];

    }
}

Then I made some changes in the testroute code under routes/web.php:

<?php

use Illuminate\Support\Facades\Route;
use App\Mail\MyTestEmail;

Route::get('/testroute', function () {

    $filePath = public_path('favicon.ico');

    $name = "Funny Coder";

    Mail::to('testreceiver@gmail.com'')->send(new MyTestEmail($name, $filePath));
});

And that’s pretty much it. If you used this code, your email should now come with an ICO attachment named favicon.ico, as you can see in the snippet above.

Sending HTML email

Sending attention-grabbing emails in Laravel is quite simple. All you have to do is add HTML code to your Blade view file. If you remember, we used test-email.blade.php in this guide.

And this is how your plain text email should look like in HTML form:

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">

    <style>
        p {
            font-size: 12px;
        }

        .signature {
            font-style: italic;
        }
    </style>
</head>
<body>
<div>
    <p>Hey {{ $name }},</p>
    <p>Can your Laravel app send emails yet? 😉 </p>
    <p class="signature">Mailtrap</p>
</div>
</body>
</html>

Send emails with embedded images

To send an email with an embedded image in Laravel, I used the embed method in the Blade template. The embed method allows you to attach images to your email content directly, which will be displayed inline.

First, ensure your mailable class (MyTestEmail in this example) passes the necessary data to the view. You don’t need to change anything in the class specifically for the image.

In your email Blade template, use the embed method to include the image. Assume you have an image at, let’s say, public/images/logo.png.

// resources/views/mail/test-email.blade.php

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
</head>
<body>
    <div>
        <p>Hey {{$name}},</p>
        <p>Can your Laravel app send emails yet? 😉</p>
        <img src="{{ $message->embed(public_path('images/logo.png')) }}" alt="Logo">
    </div>
</body>
</html>

As you can see, I essentially added this line of code:

<img src="{{ $message->embed(public_path('images/logo.png')) }}" alt="Logo">

It embeds the image located at the public/images/logo.png into the email, and the embed method returns a URL that points to the embedded image. The returned image is then used as the src attribute of the <img> tag, displaying the image inline in the email.

Queueing emails for asynchronous sending

For asynchronous email sending, I used Laravel’s queue system ShouldQueue.

So, first make sure your mailable class implements the ShouldQueue interface and use the Queueable trait:

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class MyTestEmail extends Mailable implements ShouldQueue
{
    use Queueable, SerializesModels;
    // Rest of your class...
}

Then, configure your queue driver in .env file by setting QUEUE_CONNECTION to a queue driver (e.g., database, redis, sqs, etc.).

Finally, when you send an email with the following command, Laravel will automatically queue it if the mailable class implements ShouldQueue:

Mail::to('testreceiver@example.com')->send(new MyTestEmail($name));

Note: To start your queue worker to process the queued jobs, you can run the php artisan queue:work command in your terminal.

Bulk email sending in Laravel

Remember the Mailtrap’s Bulk Stream SMTP credentials I mentioned earlier? Well, we’re going to use them to send, as you might have guessed, bulk email.

First, log into your Mailtrap account and navigate to the SMTP/API Settings tab. There, you’ll find the Bulk Stream credentials on your right.

Then, open your .env and insert these credentials. The configuration file should look something like this:

MAIL_MAILER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=your_mailtrap_bulk_username
MAIL_PASSWORD=your_mailtrap_bulk_password
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=from@example.com
MAIL_FROM_NAME="Your Application Name"
Replace your_mailtrap_bulk_username and your_mailtrap_bulk_password with your actual Mailtrap SMTP credentials for the bulk stream.

Now, if you haven’t already, you need to generate a Mailable class you’ll use for sending emails. You can customize it to suit the emails you’re planning to send in bulk, and here’s how it should look:

php artisan make:mail BulkEmail

Once you generate your BulkEmail mailable class, define the properties and methods that set up the email content. 

You can pass data to your Mailable class through its constructor and use this data within your email views.

Lastly, I looped through my recipients and sent emails individually with the SMTP connection configured for bulk use.

Here’s an example:

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

$users = User::all(); // Example: Getting all users you want to email

foreach ($users as $user) {
    Mail::to($user->email)->send(new BulkEmail($data));
}

However, there are a few things to have in mind to improve your scalability and performance:

  • Rate limiting and delays – Consider limiting rates or implementing delays between emails if you’re sending to a large number of recipients.
  • Chunking – For very large lists, Laravel’s chunk method can help you process the users in smaller batches.
    • For example:
User::chunk(200, function ($users) use ($data) {
    foreach ($users as $user) {
        Mail::to($user->email)->queue(new BulkEmail($data));
    }
});
  • Job queues – By dispatching a job for each mail, Laravel can queue the emails and send them in the background, freeing up your application to handle other requests.
    • For example:
class SendBulkEmailJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    protected $user;
    protected $data;

    public function __construct($user, $data)
    {
        $this->user = $user;
        $this->data = $data;
    }

    public function handle()
    {
        Mail::to($this->user->email)->send(new BulkEmail($this->data));
    }
}

// Dispatching the job for each user
foreach ($users as $user) {
    SendBulkEmailJob::dispatch($user, $data)->onQueue('emails');
}

Lastly, keep an eye out on your application’s logs and Mailtrap’s dashboards as it can help you ensure your emails are being processed and sent as expected.

Send email in Laravel using API

If you want to automate your sending process, Mailtrap Email Sending API is the solution you’re looking for. 

Integrating Mailtrap email API into your Laravel application is super easy as you can use the official PHP SDK and documentation for Laravel framework bridge. With these, you won’t have to manually write the integration code for your project, making the integration much easier and more efficient. Additionally, the Mailtrap library is fully compatible with Laravel 9.x and above.

To get started, simply follow these steps:

  • Create a Mailtrap account and log in.
  • Add and verify your domain.
  • Install the Mailtrap PHP client and dependencies using Composer.
composer require railsware/mailtrap-php symfony/http-client nyholm/psr7
  • Add Mailtrap transport into your config.mail.php file.
<?php

return [
    /*
    |--------------------------------------------------------------------------
    | Mailer Configurations
    |--------------------------------------------------------------------------
    */
    'mailers' => [
    
            // start mailtrap transport
            'mailtrap' => [
                'transport' => 'mailtrap'
            ],
            // end mailtrap transport
    
    ]
];
  • Add your Mailtrap credentials to your Laravel .env file (make sure to select the Transactional Stream; we will use Bulk Stream later).
MAIL_MAILER="mailtrap"
MAILTRAP_HOST="send.api.mailtrap.io"
MAILTRAP_API_KEY="YOUR_API_KEY_HERE"
MAIL_FROM_ADDRESS=”name@registered_domain.com”
  • Create a mailable class to send email.
php artisan make:mail WelcomeMail
  • Configure the app/Mail/WelcomeMail.php class following this example.
<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Attachment;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Address;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Mail\Mailables\Headers;
use Illuminate\Queue\SerializesModels;
use Mailtrap\EmailHeader\CategoryHeader;
use Mailtrap\EmailHeader\CustomVariableHeader;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\Header\UnstructuredHeader;

class WelcomeMail extends Mailable
{
    use Queueable, SerializesModels;

    private string $name;

    /**
     * Create a new message instance.
     */
    public function __construct(string $name)
    {
        $this->name = $name;
    }

    /**
     * Get the message envelope.
     */
    public function envelope(): Envelope
    {
        return new Envelope(
            from: new Address('jeffrey@example.com', 'Jeffrey Way'),
            replyTo: [
                      new Address('taylor@example.com', 'Taylor Otwell'),
                  ],
            subject: 'Welcome Mail',
            using: [
                      function (Email $email) {
                          // Headers
                          $email->getHeaders()
                              ->addTextHeader('X-Message-Source', 'example.com')
                              ->add(new UnstructuredHeader('X-Mailer', 'Mailtrap PHP Client'))
                          ;

                          // Custom Variables
                          $email->getHeaders()
                              ->add(new CustomVariableHeader('user_id', '45982'))
                              ->add(new CustomVariableHeader('batch_id', 'PSJ-12'))
                          ;

                          // Category (should be only one)
                          $email->getHeaders()
                              ->add(new CategoryHeader('Integration Test'))
                          ;
                      },
                  ]
        );
    }

    /**
     * Get the message content definition.
     */
    public function content(): Content
    {
        return new Content(
            view: 'mail.welcome-email',
            with: ['name' => $this->name],
        );
    }

    /**
     * Get the attachments for the message.
     *
     * @return array<int, \Illuminate\Mail\Mailables\Attachment>
     */
    public function attachments(): array
    {
        return [
           Attachment::fromPath('https://mailtrap.io/wp-content/uploads/2021/04/mailtrap-new-logo.svg')
                ->as('logo.svg')
                ->withMime('image/svg+xml'),
        ];
    }

    /**
     * Get the message headers.
     */
    public function headers(): Headers
    {
        return new Headers(
            'custom-message-id@example.com',
            ['previous-message@example.com'],
            [
                'X-Custom-Header' => 'Custom Value',
            ],
        );
    }
}
  • Create an email template under resources/views/mail/welcome-email.blade.php.
Hey, {{$name}} and welcome here 😉

<br>
Funny Coder
  • Add the CLI router to the app/routes/console.php file.
<?php

use App\Mail\WelcomeMail;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Mail;

/*
|--------------------------------------------------------------------------
| Console Routes
|--------------------------------------------------------------------------
|
*/

Artisan::command('send-welcome-mail', function () {
    Mail::to('testreceiver@gmail.com')->send(new WelcomeMail("Jon"));
    // Also, you can use specific mailer if your default mailer is not "mailtrap" but you want to use it for welcome mails
    // Mail::mailer('mailtrap')->to('testreceiver@gmail.com')->send(new WelcomeMail("Jon"));
})->purpose('Send welcome mail');
  • Call the CLI command to send your email.
php artisan send-welcome-mail

Once you complete these steps, you should be able to use Mailtrap’s email API for sending transactional emails without getting any errors.

Also, remember those tracking settings I mentioned earlier? You can enable them if you use the email API as well.

Emailing multiple recipients

According to Laravel’s documentation, you can send an email to multiple recipients with the following code:

foreach (['First Coder' => 'first-recipient@gmail.com', 'Second Coder' => 'second-recipient@gmail.com'] as $name => $recipient) {
    Mail::to($recipient)->send(new MyTestEmail($name));
}

You can also send your email to just one recipient and “cc” and “bcc” a few more with the following code in the MyTestEmail class:

return new Envelope(
    subject: 'My Test Email',
    cc: ['testreceiver-cc@gmail.com'],
    bcc: ['testreceiver-bcc@gmail.com']
);

Send email with attachments

Similarly, as with SMTP, I modified the attachments method to add attachments to my emails.

First, I made them in the MyTestEmail class:

<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Queue\SerializesModels;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Mail\Mailables\Attachment;
use Illuminate\Contracts\Queue\ShouldQueue;

class MyTestEmail extends Mailable
{
    use Queueable, SerializesModels;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct(private $name, public $attachedFile){ }

    /**
     * Get the message envelope.
     *
     * @return \Illuminate\Mail\Mailables\Envelope
     */
    public function envelope()
    {
        return new Envelope(
            subject: 'My Test Email',
        );
    }

    /**
     * Get the message content definition.
     *
     * @return \Illuminate\Mail\Mailables\Content
     */
    public function content()
    {
        return new Content(
            view: 'mail.test-email',
            with: ['name' => $this->name],
        );
    }

    /**
     * Get the attachments for the message.
     *
     * @return array
     */
    public function attachments()
    {
        return [
            Attachment::fromPath($this->attachedFile),
        ];

    }
}

Then, I tweaked the testroute code under routes/web.php:

<?php

use Illuminate\Support\Facades\Route;
use App\Mail\MyTestEmail;

Route::get('/testroute', function () {

    $filePath = public_path('favicon.ico');

    $name = "Funny Coder";

    Mail::to('testreceiver@gmail.com'')->send(new MyTestEmail($name, $filePath));
});

And as with SMTP, your email should now come with an ICO attachment named favicon.ico.

Sending HTML email

All you have to do to send HTML email in Laravel is add HTML code to your Blade view file, which in the case of this tutorial is test-email.blade.php.

And here’s what the plain text email looks like in HTML form:

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">

    <style>
        p {
            font-size: 12px;
        }

        .signature {
            font-style: italic;
        }
    </style>
</head>
<body>
<div>
    <p>Hey {{ $name }},</p>
    <p>Can your Laravel app send emails yet? 😉 </p>
    <p class="signature">Mailtrap</p>
</div>
</body>
</html>

Send emails with embedded images

To send an email with an embedded image, use the embed method in the Blade template to include the image. For example, let’s imagine you have an image at public/images/logo.php.

Here’s how your code would like then:

// resources/views/mail/test-email.blade.php

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
</head>
<body>
    <div>
        <p>Hey {{$name}},</p>
        <p>Can your Laravel app send emails yet? 😉</p>
        <img src="{{ $message->embed(public_path('images/logo.png')) }}" alt="Logo">
    </div>
</body>
</html>

Queuing emails for asynchronous sending

Similarly to SMTP, you can use Laravel’s ShouldQueue for async sending.

To make this work, your mailable class should implement the ShouldQueue interface and use the Queueable trait:

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class MyTestEmail extends Mailable implements ShouldQueue
{
    use Queueable, SerializesModels;
    // Rest of your class...
}

You can configure your queue driver in .env file by setting QUEUE_CONNECTION to a queue driver like database, redis, sqs, etc.

And to send an email, you can use the following command:

Mail::to('testreceiver@example.com')->send(new MyTestEmail($name));

Remember that you can run the php artisan queue:work command in your terminal to start your queue worker to process the queued job.

Bulk email sending

To start sending bulk email, you first need to insert Bulk Stream credentials, which you can find in the SMTP/API Settings tab when you log in to your Mailtrap account.

Once you find the credentials, add them to your Laravel .env file (make sure to select the Bulk Stream).

Here’s what your .env file should look like when you enter your Bulk Stream credentials:

MAIL_MAILER="mailtrap"
MAILTRAP_HOST="send.api.mailtrap.io"
MAILTRAP_API_KEY="YOUR_BULK_STREAM_API_KEY_HERE"

Once you replace your credentials, you need to generate a Mailable class for sending emails:

php artisan make:mail BulkEmail

From here, you have three options for sending bulk email, namely:

  • Looping through

If you’re sending a small amount of email in bulk, you can loop through your recipients and send emails individually. Like so:

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

$users = User::all(); // Example: Getting all users you want to email

foreach ($users as $user) {
    Mail::to($user->email)->send(new BulkEmail($data));
}
  • Chunking

If you plan to send a large amount of emails in bulk, the Chunking method can help you process the users in small batches.

Check it out:

User::chunk(200, function ($users) use ($data) {
    foreach ($users as $user) {
        Mail::to($user->email)->queue(new BulkEmail($data));
    }
});
  • Job queues

If you plan to send a large amount of emails in bulk and free up your application to handle other requests, you can dispatch a job for each mail:

class SendBulkEmailJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    protected $user;
    protected $data;

    public function __construct($user, $data)
    {
        $this->user = $user;
        $this->data = $data;
    }

    public function handle()
    {
        Mail::to($this->user->email)->send(new BulkEmail($this->data));
    }
}

// Dispatching the job for each user
foreach ($users as $user) {
    SendBulkEmailJob::dispatch($user, $data)->onQueue('emails');
}

Regardless of the method for bulk sending you choose, you should always watch out for your application’s Logs and Mailtrap’s dashboard to ensure your emails are being processed and sent. 

Moreover, consider limiting rates or implementing delays between emails if you’re sending to a large number of recipients.

Test emails and email sending: why and how

With all this code written, you wouldn’t want to have your emails sent from a blacklisted domain, marked as spam, or their HTML template poorly rendered by some web browsers. That’s why you should test your emails in Laravel before sending them.

Technically, you could do this with your personal inbox or log driver, but why affect your domain reputation and flood your inbox with junk, when you can use Mailtrap Email Testing?

Mailtrap is also supported by Laravel in the official documentation.

This Email-Sending platform also has many features that can help you solve numerous testing issues while keeping your email testing process secure.

With Mailtrap Email Testing, you can catch testing emails and preview them in a safe sandbox environment. You can also check the spam score of your emails, analyze their HTML/CSS, and more, all before sending them out.

Basically, what Mailtrap Email Testing does is it makes sure your emails land where and when they’re supposed to, instead of going to spam folders. 

Mailtrap Email Testing also lets you easily share the testing process with your team members, create new projects, and add multiple objects within.

You can also forward the emails to the real addresses manually or automatically once you’re happy with the results you’re getting.

Most importantly, testing emails with Mailtrap is quite easy.

SMTP

To test your emails with Mailtrap, all you have to do is create a free Mailtrap account, which will give you access to the entire Mailtrap Email Delivery Platform. This means you’ll be able to use both Email Testing and Email Sending.

Once you create your account and log in, simply follow these steps:

  • Navigate to Email TestingInboxesSMTP Settings
  • Select the desired Laravel version from the list of integrations (I recommend version 9+)
  • Copy the generated code snippet in the .env file located in the root directory of your project. It should look something like this:
MAIL_MAILER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=07f4dbf61b8122
MAIL_PASSWORD=d7fc5d57bc6eac
MAIL_ENCRYPTION=tls

Once you’re done, you’ll be able to send the first test email with the following code:

<?php
use App\Mail\JustTesting;
use Illuminate\Support\Facades\Mail;
Route::get('/send-mail', function () {
    Mail::to('newuser@example.com')->send(new JustTesting());
    return 'A message has been sent to Mailtrap!';
});

Pro Tip: Make sure you are using Laravel 5.8 or a newer version and that you have specified the route in the routes/web.php file.

To send the test email, start the application and access/send-mail path in your browser. You’ll soon be able to see the email in Email TestingInboxes.

API

Alternatively, you can integrate Email Testing into your app and use Testing API for testing, automation, and testing automated sequences.

Depending on which HTTP client you use, run one of the following commands:

# With symfony http client (recommend)
composer require railsware/mailtrap-php symfony/http-client nyholm/psr7

# Or with guzzle http client
composer require railsware/mailtrap-php guzzlehttp/guzzle php-http/guzzle7-adapter

Add Mailtrap transport into your config.mail.php file:

<?php

return [
    /*
    |--------------------------------------------------------------------------
    | Mailer Configurations
    |--------------------------------------------------------------------------
    */
    'mailers' => [
    
            // start mailtrap transport
            'mailtrap' => [
                'transport' => 'mailtrap'
            ],
            // end mailtrap transport
    
    ]
]

Then, you need to set the API key to the MAILTRAP_API_KEY variable and set your inbox ID to the MAILTRAP_INBOX_ID, like so:

MAIL_MAILER="mailtrap"

MAILTRAP_HOST="sandbox.api.mailtrap.io"
MAILTRAP_API_KEY="YOUR_API_KEY_HERE"
MAILTRAP_INBOX_ID=1000001

Notes

  • You can find your API key/token by going to your Mailtrap account and navigating to Sending DomainsSMTP/API Settings
  • Make sure to run the clear configuration cache command to set up new variables with:
php artisan config:clear

And to test your first email, you need to generate a Mailable class, as with sending:

php artisan make:mail WelcomeMail

Once you create your Mailable class, you can configure your email. Here’s an example:

# app/Mail/WelcomeMail.php
<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Attachment;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Address;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Mail\Mailables\Headers;
use Illuminate\Queue\SerializesModels;
use Mailtrap\EmailHeader\CategoryHeader;
use Mailtrap\EmailHeader\CustomVariableHeader;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\Header\UnstructuredHeader;

class WelcomeMail extends Mailable
{
    use Queueable, SerializesModels;

    private string $name;

    /**
     * Create a new message instance.
     */
    public function __construct(string $name)
    {
        $this->name = $name;
    }

    /**
     * Get the message envelope.
     */
    public function envelope(): Envelope
    {
        return new Envelope(
            from: new Address('jeffrey@example.com', 'Jeffrey Way'),
            replyTo: [
                      new Address('taylor@example.com', 'Taylor Otwell'),
                  ],
            subject: 'Welcome Mail',
            using: [
                      function (Email $email) {
                          // Headers
                          $email->getHeaders()
                              ->addTextHeader('X-Message-Source', 'example.com')
                              ->add(new UnstructuredHeader('X-Mailer', 'Mailtrap PHP Client'))
                          ;

                          // Custom Variables
                          $email->getHeaders()
                              ->add(new CustomVariableHeader('user_id', '45982'))
                              ->add(new CustomVariableHeader('batch_id', 'PSJ-12'))
                          ;

                          // Category (should be only one)
                          $email->getHeaders()
                              ->add(new CategoryHeader('Integration Test'))
                          ;
                      },
                  ]
        );
    }

    /**
     * Get the message content definition.
     */
    public function content(): Content
    {
        return new Content(
            view: 'mail.welcome-email',
            with: ['name' => $this->name],
        );
    }

    /**
     * Get the attachments for the message.
     *
     * @return array<int, \Illuminate\Mail\Mailables\Attachment>
     */
    public function attachments(): array
    {
        return [
            Attachment::fromPath('https://mailtrap.io/wp-content/uploads/2021/04/mailtrap-new-logo.svg')
                ->as('logo.svg')
                ->withMime('image/svg+xml'),
        ];
    }

    /**
     * Get the message headers.
     */
    public function headers(): Headers
    {
        return new Headers(
            'custom-message-id@example.com',
            ['previous-message@example.com'],
            [
                'X-Custom-Header' => 'Custom Value',
            ],
        );
    }
}

Additionally, you can use an email template:

# resources/views/mail/welcome-email.blade.php

Hey, {{$name}} and welcome here 😉

<br>
Funny Coder

And add CLI router:

# app/routes/console.php
<?php

use App\Mail\WelcomeMail;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Mail;

/*
|--------------------------------------------------------------------------
| Console Routes
|--------------------------------------------------------------------------
|
*/

Artisan::command('send-welcome-mail', function () {
    Mail::to('testreceiver@gmail.com')->send(new WelcomeMail("Jon"));
    // Also, you can use specific mailer if your default mailer is not "mailtrap" but you want to use it for welcome mails
    // Mail::mailer('mailtrap')->to('testreceiver@gmail.com')->send(new WelcomeMail("Jon"));
})->purpose('Send welcome mail');

And to send your email, just call this CLI command:

php artisan send-welcome-mail

For additional information, have a look at the official documentation for Mailtrap API.

Wrapping up

And with that, we wrap up our ‘Laravel send email’ guide.

We’ve covered a lot, from configuring Laravel to sending email with SMTP to sending email via API. Regardless of the option you choose, you can always turn to this article for step-by-step instructions.

To learn more about Laravel, check out the official documentation. And if you’re looking for more content about sending email with Laravel, make sure to visit the Mailtrap blog where you can find more useful articles such as:

Article by Aleksandr Varnin Full Stack Developer @ Railsware
Ivan Djuric, an author at Mailtrap
Article by Ivan Djuric Technical Content Writer @Mailtrap

I’m a Technical Content Writer with 5 years of background covering email-related topics in tight collaboration with software engineers and email marketers. I just love to research and share actionable insights with you about email sending, testing, deliverability improvements, and more. Happy to be your guide in the world of emails!

Comments

4 replies

Mouhsen Ibrahim

Hi, thanks for the nice article, it was helpful for me, but there is an error in your code in the build method of the Mailable class it should be as follows

return $this->from(‘example@example.com’)
->markdown(’emails.newuser’);

use markdown instead of view when creating the email using markdown.

Piotr Malek

Hey! Thanks for your comment and I’m really glad you found your article useful.

You’re absolutely right that one should use ‘markdown’ method with Markdown templates. You can see the example of it in the last paragraph of this article: https://blog.mailtrap.io/send-email-in-laravel/#How_to_send_email_in_Laravel_70_using_SMTP

The ‘view’ method, however, is used to specify the template when rendering the content of an email (built with Blade, for example), as demonstrated in the Laravel docs: https://laravel.com/docs/7.x/mail#configuring-the-view

So to my knowledge, the sample above is correct. If you spot any inconsistencies here or in other articles, please us know. Thanks!

Keanu Reeves

Thank you !!!

Belen

Muchas gracias!!! me ha sido de gran ayuda tu artículo.

Comments are closed.