c975l / contactform-bundle
Bundle to manage simple contact form
Package info
github.com/975L/ContactFormBundle
Type:symfony-bundle
pkg:composer/c975l/contactform-bundle
Requires
This package is auto-updated.
Last update: 2026-06-27 18:48:08 UTC
README
Symfony bundle that provides a fully-featured contact form with built-in spam protection, reCaptcha v3, rate limiting, event-driven customization, and multilingual support.
Features
- Contact form at
/contact(or/{_locale}/contactfor multilingual apps) - Pre-fills name and email when a user is logged in
- Sends emails via Symfony Mailer (
TemplatedEmail) - Dispatches events to customize form behavior and email content
- Anti-spam: dynamic honeypot with randomized field names and labels per session
- Anti-spam: minimum submission delay check to reject bot submissions
- Anti-spam: reCaptcha v3 via
karser/KarserRecaptcha3Bundle - Rate limiting: optional limits by IP and by email address
- GDPR consent checkbox (configurable)
- Optional "receive a copy" checkbox for the sender
- Subject pre-fill via URL parameter (
?s=My+Subject) - Configuration managed via c975L/ConfigBundle
Requirements
- PHP >= 8.0
- Symfony >= 7.0
- c975L/ConfigBundle
- c975L/SiteBundle
- karser/karser-recaptcha3-bundle
Installation
Download
composer require c975l/contactform-bundle
Enable routes
Add the following to config/routes.yaml:
c975_l_contact_form: resource: "@c975LContactFormBundle/" type: attribute prefix: / # For multilingual websites: # prefix: /{_locale} # defaults: { _locale: '%locale%' } # requirements: # _locale: en|fr|es
Load configuration values
php bin/console c975l:config:load-all
Then use the ConfigBundle dashboard to set the values for each key.
Configure reCaptcha v3
Create your keys on Google reCaptcha and set them in your .env.local:
RECAPTCHA3_KEY=your_site_key RECAPTCHA3_SECRET=your_secret_key
Or store them via the ConfigBundle dashboard (recaptcha3-site-key and recaptcha3-secret-key).
Override the layout template
Create templates/bundles/c975LContactFormBundle/layout.html.twig and extend your own layout:
{% extends 'layout.html.twig' %}
{% set title = 'Contact' %}
{% block content %}
{% block contactform_content %}
{% endblock %}
{% endblock %}
The email templates are loaded from SiteBundle. Override them in templates/c975LSiteBundle/emails/.
Usage
The route name is contactform_display. Link to it from Twig:
{{ path('contactform_display') }}
Pre-filling the subject
Pass the s query parameter to pre-fill the subject field (rendered as read-only):
https://example.com/contact?s=My+Subject
Rate limiting (optional)
If the following Symfony RateLimiter services are defined, they are automatically applied before any email is sent:
limiter.contact_form_by_iplimiter.contact_form_by_email
Example (config/packages/rate_limiter.yaml):
framework: rate_limiter: contact_form_by_ip: policy: sliding_window limit: 5 interval: '10 minutes' contact_form_by_email: policy: sliding_window limit: 3 interval: '10 minutes'
Honeypot and CSP
If you have disabled unsafe-inline for style-src in your Content Security Policy, add this rule to keep the honeypot hidden:
.sr-only { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
Events
Two events allow customization without modifying the bundle:
| Constant | Event name | Fired when |
|---|---|---|
ContactFormEvent::CREATE_FORM |
c975l_contactform.create.form |
The form is being built |
ContactFormEvent::SEND_FORM |
c975l_contactform.send.form |
The form has been submitted and validated |
Example: customize email on submission
namespace App\EventSubscriber; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use c975L\ContactFormBundle\Event\ContactFormEvent; class ContactFormSubscriber implements EventSubscriberInterface { public static function getSubscribedEvents(): array { return [ ContactFormEvent::SEND_FORM => 'onSendForm', ]; } public function onSendForm(ContactFormEvent $event): void { $subject = $event->getFormData()->getSubject(); if (str_contains((string) $subject, 'some-keyword')) { $event->setEmailData([ 'subject' => 'Custom email subject', 'bodyEmail' => 'emails/custom_contact.html.twig', 'bodyData' => [], ]); // Or abort sending with an error code: // $event->setError('error.user_not_found'); } } }
Override the redirect URL after submission
public function onCreateForm(ContactFormEvent $event): void { $event->getRequest()->getSession()->set('redirectUrl', 'https://example.com/thank-you'); }
If this bundle saves you development time, consider sponsoring via the Sponsor button at the top of the repository. Thank you!