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-phpMail 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-messageFile: 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 }} <{{ $senderEmail }}>
{{ $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 pailCheck 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:checkCommit
bash
git add -A
git commit -m '06 - Mail Setup'