Create a contact form with Laravel

In this article we will create a working contact form with Laravel 9. At the bottom of the post you can download the finished project.

First, we need to create a new Laravel Project. If you have the Laravel installer installed you can use the following command:

laravel new [your_project_name]

if not, use composer to install Laravel:

composer create-project laravel/laravel [your_project_name]

Once installed, make sure to fill out your database details in your .env file, although we are not going to need database in this tutorial.

Test SMTP details

Once we are ready with the contact form we will need to test if the emails are sent properly. In order to do that we will need some kind of SMTP service. For this we can use Mailtrap. Go ahead and create an account. Once your account is created, you’ll be provided with SMTP details that you can add to your .env file. You will need to provide the followings:


Contact controller

Let’s create a contact controller. In your terminal type:

php artisan make:controller ContactController

First we need to import Laravel’s email API. At the top of the controller file, add:

use Mail;

In the ContactController we will need two methods, one for displaying the contact form, and another to send the actual data.

First create a simple index method that will return a view.

public function index()
return view('contact');

Now, let’s go and create the view file.

The Contact View and Routes

In the resources -> views folder, create a file called “contact.blade.php”

In this example, I’m not going to create any layout so just add and HTML boilerplate structure to the file. In the head section I’ll just add a bootstrap cdn link like this:

<link href="" rel="stylesheet">

and before the closing body tag I’ll just add the Bootstrap javascript cdn link:

<script src=""></script>

Before we add anything to the view, quickly go to the “routes” folder and open the web.php file. At the top pull in the ContactController file.

use App\Http\Controllers\ContactController;

and create a route that will handle the index method

Route::get('contact', [ContactController::class, 'index'])->name('contact');

Now we can check if the view is loaded ok. Add some temporary text to the view file and start the Laravel server in your terminal: php artisan serve

and try to access the page via

if it loads ok, let’s create the form. Add the following content inside the body of the contact.blade.php

<div class="container">

<h1 class="text-center mt-3">Contact us</h1>

<form action="" method="POST">

<div class="mb-3">
<label for="firstName" class="form-label">Your name</label>
<input type="text" name="fname" class="form-control @error('fname') is-invalid @enderror" id="firstName" value="{{old('fname')}}">
<div style="color: red; font-size: 13px;">{{ $message }}</div>
<div class="mb-3">
<label for="yourEmail" class="form-label">Your email</label>
<input type="email" name="email" class="form-control @error('email') is-invalid @enderror" id="yourEmail" placeholder="" value="{{old('email')}}">
<div style="color: red; font-size: 13px;">{{ $message }}</div>
<div class="mb-3">
<label for="subject" class="form-label">Subject</label>
<input type="text" name="subject" class="form-control @error('subject') is-invalid @enderror" id="subject" value="{{old('subject')}}">
<div style="color: red; font-size: 13px;">{{ $message }}</div>
<div class="mb-3">
<label for="messageBody" class="form-label">Your message</label>
<textarea class="form-control @error('messageBody') is-invalid @enderror" name="messageBody" id="messageBody" rows="5">{{old('messageBody')}}</textarea>
<div style="color: red; font-size: 13px;">{{ $message }}</div>

<button type="submit" class="btn btn-primary">Send message</button>


Here we only have three fields, a first name, email and message. We use Laravel’s old helper to retain the old data in case the form encounters any problem and is not sent. And we also display some error messages if the formsubmission fails.

Now let’s go back to the controller file.

Create sendForm method

In our controller file we create our second method called sendForm. Here is the entire code for this method:

public function sendForm(Request $request)

$this->validate($request, [
'fname' => 'required|string|min:1',
'email' => 'required|email:filter',
'subject' => 'required|string|min:1',
'messageBody' => 'required|min:3',

$siteName = env('APP_NAME', '');

$data = array(
'fname' => $request->fname,
'email' => $request->email,
'subject' => $siteName . ' - ' . $request->subject,
'messageBody' => nl2br(htmlspecialchars($request->messageBody)),
'subject_without_site_title' => $request->subject

if( env('MAIL_FROM_ADDRESS') != '' )
$mailFrom = env('MAIL_FROM_ADDRESS');

Mail::send('', $data, function($message) use ($data, $mailFrom) {
catch(\Exception $e)
return redirect()->back()->with('failureMsg', 'Message not sent!')->withInput();
return redirect()->back()->with('failureMsg', 'Message not sent!')->withInput();

return redirect()->back()->with('successMsg', 'Message sent successfully!');


First we add “Request” to the method in order to have access to all the request data and then we validate the data coming from our contact form. After this we store our APP_NAME in a variable called “siteName”. Make sure you added your app name/site title in your .env file.

Then we store our data from the contact form in an array.

Now the “subject_without_site_title” key doesn’t come from our form. It will be used in the contact email content because the “subject” also contains the site name.

In an if statement we check if the MAIL_FROM_ADDRESS in the .env file is not empty. If not, we store that email address in a variable and send the email message in a try-catch statement using Laravel’s email API.

The “send” method accepts three arguments. The first one is the view file that contains the content of the email that will be sent. The second is the data that we just validated, and this data will also be passed to the view file. The third argument is a closure that allows us to send header information such as from, to, subject etc.

In case of a submission failure, we redirect the user to the contact page with error messages.

The content of the email that will be sent is going to be another blade file which we will create in a second and it’s going to be in the “resources -> views -> emails” folder. Here we will create another contact.blade.php file.

The content of this file will be:

<h2 style="margin-top: 25px;">Hello</h2>

<p>You are receiving this email from: {{ env('APP_URL') }}</p>
<p>Message details:</p>

<p><strong>Message sent by:</strong> {{ $fname }}</p>
<p><strong>Subject:</strong> {{ $subject_without_site_title }}</p>
<p><strong>Sender's email:</strong> {{ $email }}</p>
<hr style="height: 1px; background-color: #9cc1d3; border: none;">
<p><strong>Message content:</strong></p>
{!! $messageBody !!}

Of course, you can style it any way you want. Here we can use the array keys as variables to output the data from the submitted contact form.

We are almost done. We only have a few small things to do.

Let’s go to the web.php file and add a route for posting the form.

Route::post('contact/send', [ContactController::class, 'sendForm'])->name('sendcontact');

We need to add this route to the form action, so go back to the contact.blade.php where we have the form and add the following route to the action attribute:

<form action="{{ route('sendcontact') }}" method="POST">

If you look at our sendForm method we have error and success messages. We need to be able to display those messages in our view. Add the following code above the “form” tag:

<div class="alert alert-success alert-dismissible fade show mt-1" role="alert">
<strong>{{ session('successMsg') }}</strong>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>

<div class="alert alert-danger alert-dismissible fade show mt-1" role="alert">
<strong>{!! session('failureMsg') !!}</strong>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>


Now, you can test email sending. If you don’t fill out the form and try to send it, you’ll receive the error messages displayed under each field in the view.

Download finished project here!


Christian Kovats is a London-based web designer who loves crafting innovative and user-friendly interfaces. His passion for design is only rivaled by his love for nature and hiking, often drawing inspiration from his treks through the diverse landscapes of the UK. Outside of work, Christian enjoys spending quality time with his wife and 4-year-old son. A seasoned lucid dreamer, he also spends time exploring the intricate world of dream interpretation, reflecting his ever-curious and creative mind.


Leave a reply

You must be logged in to post a comment.