Building a Contact Form in Laravel with Filament (Step-by-Step)
Building a Contact Form in Laravel with Filament (Step-by-Step)
Every Laravel app eventually needs a contact form. And every time, you end up writing the same boilerplate: a controller, a form request, validation rules, an email notification, a Blade view, maybe a CSRF token check you forgot about. It works, but it's a lot of wiring for something this common. In this tutorial, I'll build a fully working contact form using FilaForms, a form builder plugin for Filament. We'll go from installation to a live, public-facing form with email notifications in about 15 minutes. ## Prerequisites You need: - PHP 8.3+ - Laravel 11 or 12 - Filament 5.x installed and configured - A working mail driver (Mailgun, Postmark, SMTP, whatever you use) If you don't have Filament set up yet, follow the [official Filament docs](https://filamentphp.com/docs) first. This tutorial assumes you already have a Filament panel running. ## Step 1: Install FilaForms Pull in the package: bash composer require filaforms/core Publish the config and run migrations: bash php artisan vendor:publish --tag="filaforms-config" php artisan migrate Register the plugin in your panel provider: php use FilaForms\\Core\\FilaFormsPlugin; public function panel(Panel $panel): Panel { return $panel ->plugins(\[ FilaFormsPlugin::make(), \]); } You should now see a "Forms" section in your Filament sidebar. ## Step 2: Create the Contact Form Click "Forms" in the sidebar, then hit "Create." Give it a name like "Contact Form." You could start from scratch, but FilaForms ships with 20 pre-built templates, and one of them is a Contact Form template. Select it, and you'll get a form pre-populated with the fields you'd expect: name, email, subject, and message. If you want to build it manually instead, here's how I'd set it up using the drag-and-drop builder: 1. Add a **Text** field. Label it "Name." Mark it as required. 2. Add an **Email** field. Label it "Email Address." Mark it as required, and the email validation rule is applied automatically. 3. Add a **Text** field for "Subject." Required. 4. Add a **Textarea** field for "Message." Set a minimum length if you want to avoid one-word submissions (the min validation rule works well here). For layout, I'd put Name and Email side by side at 50% width each (FilaForms uses a 12-column grid, so that's 6 columns per field). Subject and Message get full width. This keeps the form compact without feeling cramped. The builder gives you a live preview as you add fields, so you can see exactly what respondents will see. ## Step 3: Add Validation Rules Each field has a validation tab in the builder. For a contact form, I'd set: - **Name**: required, max:255 - **Email**: required, email (this one is on by default for email fields) - **Subject**: required, max:255 - **Message**: required, min:10, max:5000 FilaForms supports about 30 built-in validation rules, including regex if you need something specific. But for a contact form, the basics are enough. ## Step 4: Configure Email Notifications This is the part that usually takes the most code when [building forms from scratch](https://filaforms.app/blog/filaforms-vs-building-forms-from-scratch-in-laravel). With FilaForms, it's a settings panel. ### Admin Notification In the form's notification settings, enable the admin email notification. You'll configure: - **Recipient email(s)**: Where submissions should go. You can add multiple addresses. - **CC/BCC**: If you need to loop in other team members. - **From email and name**: Set these to match your domain (e.g., noreply@yourapp.com). - **Reply-to**: Set this to the respondent's email field so you can reply directly from your inbox. - **Subject line**: Something like New contact form submission: {form\_name} works. You can use merge tags like {form\_name}, {submission\_id}, and {date}. The admin email includes all submitted field values, so you don't need to customize the body. ### Auto-Response to the Respondent FilaForms can also send an automatic reply to whoever filled out the form. Enable the auto-response option and select which field contains the respondent's email address (in our case, the "Email Address" field). You can customize the subject and body. The body supports merge tags using the {{field\_code}} syntax, so you could write something like: > Hi {{name}}, thanks for reaching out. We received your message about "{{subject}}" and will get back to you within 24 hours. This is optional, but it's a nice touch that tells people their submission actually went through. ### In-App Notifications FilaForms also sends in-app notifications through Filament's notification system. If you're already logged into your admin panel, you'll see new submissions appear in the notification bell. No extra setup needed. ## Step 5: Enable Spam Protection FilaForms includes honeypot protection out of the box. It adds a hidden field to your form that's invisible to real users but gets filled in by bots. When a bot submits the form, FilaForms silently rejects it. No CAPTCHA, no friction for real users. This is enabled by default on all forms. For most contact forms, it's enough. If you're getting targeted spam, you might want to add rate limiting at the middleware level, but the honeypot handles the generic bot traffic. ## Step 6: Set the Post-Submit Behavior After someone submits the form, you have two options: 1. **Show a message**: Display a confirmation like "Thanks for your message. We'll get back to you soon." This is the default and works well for most cases. 2. **Redirect**: Send them to a specific URL, like a thank-you page. Useful if you want to track conversions or show additional information. Pick whichever fits your app. I usually go with the message for contact forms since there's no reason to redirect. ## Step 7: Make It Public Your form needs a URL that unauthenticated users can access. FilaForms gives every form a public URL at /filaforms/{ulid}. Toggle the form's visibility to public, and it's live. If you've read the tutorial on [adding public-facing forms to your Filament app](https://filaforms.app/blog/how-to-add-public-facing-forms-to-your-filament-app), this part is familiar. The ULID-based URL works immediately with no extra routes or middleware to configure. For a contact form on your marketing site, though, you probably don't want to send people to /filaforms/01HQXYZ.... You want the form embedded in an existing page. That brings us to the next step. ## Step 8: Embed the Form in a Blade View To put the contact form on your /contact page (or anywhere else in your Laravel app), use the Livewire component that FilaForms provides: php // routes/web.php use App\\Models\\FilaForms\\Form; Route::get('/contact', function () { $form = Form::where('slug', 'contact-form')->firstOrFail(); return view('contact', \['form' => $form\]); }); Then in your Blade template: ```blade {{-- resources/views/contact.blade.php --}} @extends('layouts.app') @section('content')
#Contact Us
@endsection ``` That's it. The component handles rendering, validation, submission, and notifications. You write one route and a few lines of Blade. The form looks and behaves exactly like it does in the builder preview. If you need the form on multiple pages, just pass the same $form model to the component wherever you need it. It's a standard Livewire component, so it works anywhere Livewire runs. ## What You Get At this point, you have a contact form that: - Validates input on both client and server side - Sends an email notification to your team when someone submits - Optionally sends a confirmation email to the respondent - Rejects bot submissions with honeypot protection - Stores every submission in your database (viewable in Filament, exportable as CSV or Excel) - Lives on a public URL or embedded in any Blade view All without writing a single controller, form request class, or Mailable. The next time the requirements change (add a phone field, change who gets notified, update the confirmation message), you make the change in the builder. No code to deploy. ## Viewing and Exporting Submissions One thing worth mentioning: every submission is stored as JSON in your database and shows up in the Filament panel under your form's submissions tab. You can browse, search, and export them to CSV or Excel directly from the admin panel. This means your contact form doubles as a lightweight CRM of sorts. You have a record of every inquiry, when it came in, and what was said. For a lot of small apps, that's all you need. --- If you're building forms in a Filament app and want to skip the repetitive setup work, [FilaForms](https://filaforms.app) handles the plumbing so you can focus on the parts that actually matter.