Skip to content

Mail Setup ​

Wire up the contact form from step 05. Use the log mailer first, then test Resend delivery.

Install Resend ​

Use Laravel's built-in transport; only the PHP SDK is needed.

bash
composer require resend/resend-php

Mail Configuration ​

Before live delivery

Replace MAIL_CONTACT_TO with the receiving inbox and MAIL_FROM_ADDRESS with a sender on the verified Resend domain. Keep the real RESEND_API_KEY only in .env, not .env.example or Git.

Environment ​

Merge only the mail settings into .env.example and .env. Keep placeholders in .env.example and MAIL_MAILER=log locally until ready to send; do not overwrite APP_KEY or database settings.

File: .env.example

dotenv
APP_NAME="Vehub"
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=https://vehub-app.test

APP_LOCALE=en
APP_FALLBACK_LOCALE=en
APP_FAKER_LOCALE=en_US

APP_MAINTENANCE_DRIVER=file
# APP_MAINTENANCE_STORE=database

# PHP_CLI_SERVER_WORKERS=4

BCRYPT_ROUNDS=12

LOG_CHANNEL=stack
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=vehub_app
DB_USERNAME=root
DB_PASSWORD=

SESSION_DRIVER=database
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=null

BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local
QUEUE_CONNECTION=database

CACHE_STORE=database
# CACHE_PREFIX=

MEMCACHED_HOST=127.0.0.1

REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379

MAIL_MAILER=resend
MAIL_SCHEME=null
MAIL_HOST=127.0.0.1
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_CONTACT_TO="hello@example.com"
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"
RESEND_API_KEY=your-resend-api-key

AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false

VITE_APP_NAME="${APP_NAME}"

Mail Config ​

Add the top-level contact entry. The baseline already has the resend mailer and services.resend.key wired to RESEND_API_KEY; confirm both are still present.

File: config/mail.php

php
<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Default Mailer
    |--------------------------------------------------------------------------
    |
    | This option controls the default mailer that is used to send all email
    | messages unless another mailer is explicitly specified when sending
    | the message. All additional mailers can be configured within the
    | "mailers" array. Examples of each type of mailer are provided.
    |
    */

    'default' => env('MAIL_MAILER', 'log'),

    /*
    |--------------------------------------------------------------------------
    | Mailer Configurations
    |--------------------------------------------------------------------------
    |
    | Here you may configure all of the mailers used by your application plus
    | their respective settings. Several examples have been configured for
    | you and you are free to add your own as your application requires.
    |
    | Laravel supports a variety of mail "transport" drivers that can be used
    | when delivering an email. You may specify which one you're using for
    | your mailers below. You may also add additional mailers if needed.
    |
    | Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2",
    |            "postmark", "resend", "log", "array",
    |            "failover", "roundrobin"
    |
    */

    'mailers' => [

        'smtp' => [
            'transport' => 'smtp',
            'scheme' => env('MAIL_SCHEME'),
            'url' => env('MAIL_URL'),
            'host' => env('MAIL_HOST', '127.0.0.1'),
            'port' => env('MAIL_PORT', 2525),
            'username' => env('MAIL_USERNAME'),
            'password' => env('MAIL_PASSWORD'),
            'timeout' => null,
            'local_domain' => env('MAIL_EHLO_DOMAIN', parse_url((string) env('APP_URL', 'http://localhost'), PHP_URL_HOST)),
        ],

        'ses' => [
            'transport' => 'ses',
        ],

        'postmark' => [
            'transport' => 'postmark',
            // 'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'),
            // 'client' => [
            //     'timeout' => 5,
            // ],
        ],

        'resend' => [
            'transport' => 'resend',
        ],

        'sendmail' => [
            'transport' => 'sendmail',
            'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'),
        ],

        'log' => [
            'transport' => 'log',
            'channel' => env('MAIL_LOG_CHANNEL'),
        ],

        'array' => [
            'transport' => 'array',
        ],

        'failover' => [
            'transport' => 'failover',
            'mailers' => [
                'smtp',
                'log',
            ],
            'retry_after' => 60,
        ],

        'roundrobin' => [
            'transport' => 'roundrobin',
            'mailers' => [
                'ses',
                'postmark',
            ],
            'retry_after' => 60,
        ],

    ],

    /*
    |--------------------------------------------------------------------------
    | Global "From" Address
    |--------------------------------------------------------------------------
    |
    | You may wish for all emails sent by your application to be sent from
    | the same address. Here you may specify a name and address that is
    | used globally for all emails that are sent by your application.
    |
    */

    'from' => [
        'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
        'name' => env('MAIL_FROM_NAME', env('APP_NAME', 'Laravel')),
    ],

    /*
    |--------------------------------------------------------------------------
    | Contact Address
    |--------------------------------------------------------------------------
    |
    | You may wish for all emails sent by your application to have a contact
    | address. Here you may specify the address that is used globally for
    | all emails that are sent by your application.
    |
    */
    'contact' => [
        'to' => env('MAIL_CONTACT_TO'),
    ],
];

An empty MAIL_CONTACT_TO falls back to mail.from.address. Resend ignores the SMTP host, port, and password settings.

Create a Mailable ​

bash
php artisan make:mail ContactMessage --markdown=mail.contact-message

File: app/Mail/ContactMessage.php

php
<?php

namespace App\Mail;

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

class ContactMessage extends Mailable
{
    use Queueable, SerializesModels;

    /**
     * Create a new message instance.
     */
    public function __construct(
        public string $senderName,
        public string $senderEmail,
        public string $body,
    ) {}

    /**
     * Get the message envelope.
     */
    public function envelope(): Envelope
    {
        return new Envelope(
            subject: "Contact message from {$this->senderName}",
            replyTo: [new Address($this->senderEmail, $this->senderName)],
        );
    }

    /**
     * Get the message content definition.
     */
    public function content(): Content
    {
        return new Content(
            markdown: 'mail.contact-message',
        );
    }

    /**
     * Get the attachments for the message.
     *
     * @return array<int, Attachment>
     */
    public function attachments(): array
    {
        return [];
    }
}

File: resources/views/mail/contact-message.blade.php

blade
<x-mail::message>
# New contact message

**From:** {{ $senderName }} &lt;{{ $senderEmail }}&gt;

{{ $body }}

Thanks,<br>
{{ config('app.name') }}
</x-mail::message>

Enable Contact Mail ​

Enable the send in the contact controller. Keep validation and flash success only after send() returns. Sending is synchronous; no queue worker needed.

Keep the verified address as From and the visitor's address as Reply-To.

File: app/Http/Controllers/ContactFormController.php

php
<?php

namespace App\Http\Controllers;

use App\Mail\ContactMessage;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
use Inertia\Inertia;

class ContactFormController extends Controller
{
    public function store(Request $request): RedirectResponse
    {
        $validated = $request->validate([
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'email', 'max:255'],
            'message' => ['required', 'string', 'max:5000'],
        ]);

        $recipient = config('mail.contact.to') ?: config('mail.from.address');

        Mail::to($recipient)->send(new ContactMessage(
            senderName: $validated['name'],
            senderEmail: $validated['email'],
            body: $validated['message'],
        ));

        Inertia::flash('toast', [
            'type' => 'success',
            'message' => __('Thanks! Your message has been sent.'),
        ]);

        return back();
    }
}

Test Mail Locally ​

With MAIL_MAILER=log, clear config, start the log viewer, and submit the form:

bash
php artisan config:clear
php artisan pail

Check recipient, From, Reply-To, subject, and body in the log. The log mailer writes the message content locally; use dummy data.

Set MAIL_MAILER=resend, clear config again, and send a real delivery test. Confirm receipt and reply routing; the success toast only confirms the send call returned, not inbox delivery.

Verify ​

bash
composer lint
composer ci:check

Commit ​

bash
git add -A
git commit -m '06 - Mail Setup'

References ​