Contact Form Implementation
Contact form is a basic element for user communication. Minimum set: fields (name, email, message), spam protection, submission confirmation, email notification to recipient.
Laravel: Form Processing
class ContactController extends Controller
{
public function store(ContactRequest $request): RedirectResponse|JsonResponse
{
// Save inquiry to database
$inquiry = ContactInquiry::create([
'name' => $request->name,
'email' => $request->email,
'phone' => $request->phone,
'subject' => $request->subject,
'message' => $request->message,
'ip' => $request->ip(),
'user_agent' => $request->userAgent(),
]);
// Send notification to managers
Mail::to(config('mail.contact_recipients'))
->send(new ContactInquiryMail($inquiry));
// Auto-reply to sender
Mail::to($inquiry->email)
->send(new ContactAutoReplyMail($inquiry));
if ($request->expectsJson()) {
return response()->json(['message' => 'Your message has been sent']);
}
return back()->with('success', 'Thank you! We will contact you within 24 hours.');
}
}
// app/Http/Requests/ContactRequest.php
class ContactRequest extends FormRequest
{
public function rules(): array
{
return [
'name' => 'required|string|max:100',
'email' => 'required|email:rfc,dns|max:255',
'phone' => 'nullable|string|max:20',
'subject' => 'nullable|string|max:200',
'message' => 'required|string|min:10|max:5000',
];
}
public function messages(): array
{
return [
'name.required' => 'Please enter your name',
'email.required' => 'Please enter your email',
'email.email' => 'Invalid email format',
'message.required' => 'Please write a message',
'message.min' => 'Message is too short (minimum 10 characters)',
];
}
}
Rate Limiting and Spam Protection
// routes/web.php
Route::post('/contact', [ContactController::class, 'store'])
->middleware(['throttle:5,60']); // 5 submissions per hour from one IP
// In ContactRequest
public function withValidator(Validator $validator): void
{
$validator->after(function (Validator $v) {
// Honeypot field: bots fill hidden fields
if ($this->input('website')) { // hidden field, humans don't fill it
$v->errors()->add('spam', 'Spam detected');
}
});
}
React: Form with Validation
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
const schema = z.object({
name: z.string().min(2, 'Enter name').max(100),
email: z.string().email('Invalid email'),
message: z.string().min(10, 'Minimum 10 characters').max(5000),
website: z.string().max(0), // honeypot
});
type FormData = z.infer<typeof schema>;
export function ContactForm() {
const { register, handleSubmit, formState: { errors, isSubmitting }, reset } = useForm<FormData>({
resolver: zodResolver(schema),
});
const [success, setSuccess] = useState(false);
const onSubmit = async (data: FormData) => {
await api.post('/contact', data);
setSuccess(true);
reset();
};
if (success) {
return <p role="status">Thank you! We will contact you within 24 hours.</p>;
}
return (
<form onSubmit={handleSubmit(onSubmit)} noValidate>
{/* Honeypot */}
<div style={{ display: 'none' }} aria-hidden>
<input {...register('website')} tabIndex={-1} autoComplete="off" />
</div>
<div>
<label htmlFor="name">Name *</label>
<input id="name" {...register('name')} aria-describedby={errors.name ? 'name-error' : undefined} />
{errors.name && <p id="name-error" role="alert">{errors.name.message}</p>}
</div>
<div>
<label htmlFor="email">Email *</label>
<input id="email" type="email" {...register('email')} />
{errors.email && <p role="alert">{errors.email.message}</p>}
</div>
<div>
<label htmlFor="message">Message *</label>
<textarea id="message" rows={5} {...register('message')} />
{errors.message && <p role="alert">{errors.message.message}</p>}
</div>
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? 'Sending...' : 'Send'}
</button>
</form>
);
}
Implementation Timeline
Contact form with validation, honeypot, email notification and auto-reply for Laravel + React: 1–2 days. With database storage and inquiry management panel: 2–3 days.







