Contact Form
A comprehensive contact form component with built-in validation, file uploads, spam protection, and email service integration.
Features
- Field Validation: Email, phone, required fields with real-time feedback
- File Uploads: Attach documents, images with size/type validation
- Spam Protection: reCAPTCHA, honeypot, rate limiting
- Email Integration: SendGrid, Resend, SMTP support
- Auto-save Drafts: Save form data to prevent data loss
- Multi-step Forms: Break long forms into steps
- Accessibility: WCAG 2.1 AA compliant
- Custom Fields: Add dynamic fields based on inquiry type
Installation
pnpm add @mycuppa/contact-form
Basic Usage
import { ContactForm } from '@mycuppa/contact-form'
export function SimpleContact() {
const handleSubmit = async (data) => {
console.log('Form submitted:', data)
// Send to your backend or email service
}
return (
<ContactForm
onSubmit={handleSubmit}
fields={['name', 'email', 'message']}
/>
)
}
With All Fields
import { ContactForm, FormField } from '@mycuppa/contact-form'
const fields: FormField[] = [
{ name: 'name', type: 'text', label: 'Full Name', required: true },
{ name: 'email', type: 'email', label: 'Email Address', required: true },
{ name: 'phone', type: 'tel', label: 'Phone Number' },
{
name: 'inquiryType',
type: 'select',
label: 'Inquiry Type',
options: [
{ value: 'general', label: 'General Question' },
{ value: 'support', label: 'Technical Support' },
{ value: 'sales', label: 'Sales Inquiry' },
],
},
{ name: 'company', type: 'text', label: 'Company' },
{ name: 'message', type: 'textarea', label: 'Message', required: true, rows: 5 },
{ name: 'attachment', type: 'file', label: 'Attach File', accept: '.pdf,.doc,.docx' },
]
export function ContactUs() {
const handleSubmit = async (data) => {
const response = await fetch('/api/contact', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
})
if (response.ok) {
alert('Message sent successfully!')
}
}
return (
<div className="max-w-2xl mx-auto p-6">
<h2 className="text-2xl font-bold mb-6">Get in Touch</h2>
<ContactForm
fields={fields}
onSubmit={handleSubmit}
submitButtonText="Send Message"
successMessage="Thank you! We'll get back to you soon."
/>
</div>
)
}
With Email Integration (SendGrid)
import { ContactForm } from '@mycuppa/contact-form'
import { sendEmail } from '@/lib/email'
export function ContactWithEmail() {
const handleSubmit = async (data) => {
await sendEmail({
to: 'support@example.com',
from: data.email,
subject: `New Contact Form: ${data.inquiryType}`,
html: `
<h2>New Contact Form Submission</h2>
<p><strong>Name:</strong> ${data.name}</p>
<p><strong>Email:</strong> ${data.email}</p>
<p><strong>Phone:</strong> ${data.phone || 'N/A'}</p>
<p><strong>Message:</strong></p>
<p>${data.message}</p>
`,
})
}
return <ContactForm onSubmit={handleSubmit} />
}
Multi-Step Form
import { ContactForm, FormStep } from '@mycuppa/contact-form'
const steps: FormStep[] = [
{
title: 'Your Information',
fields: ['name', 'email', 'phone'],
},
{
title: 'Your Inquiry',
fields: ['inquiryType', 'company', 'message'],
},
{
title: 'Review',
fields: [], // Review step
},
]
export function MultiStepContact() {
return (
<ContactForm
steps={steps}
showProgress
onSubmit={handleSubmit}
/>
)
}
With reCAPTCHA
import { ContactForm } from '@mycuppa/contact-form'
export function ProtectedContact() {
return (
<ContactForm
onSubmit={handleSubmit}
recaptcha={{
siteKey: process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY,
version: 'v3',
}}
/>
)
}
Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| fields | FormField[] \| string[] | Required | Form fields to display |
| steps | FormStep[] | - | Multi-step configuration |
| onSubmit | (data: FormData) => Promise<void> | Required | Submit handler |
| submitButtonText | string | 'Submit' | Submit button label |
| successMessage | string | 'Thank you for your message!' | Success message |
| errorMessage | string | 'Something went wrong. Please try again.' | Error message |
| recaptcha | RecaptchaConfig | - | reCAPTCHA configuration |
| showProgress | boolean | false | Show progress indicator |
| autoSave | boolean | false | Auto-save form data |
| resetOnSuccess | boolean | true | Reset form after submit |
Field Types
FormField Object
interface FormField {
name: string
type: 'text' | 'email' | 'tel' | 'textarea' | 'select' | 'file' | 'checkbox'
label: string
placeholder?: string
required?: boolean
validation?: (value: any) => string | undefined
options?: Array<{ value: string; label: string }> // For select
accept?: string // For file input
rows?: number // For textarea
}
Validation
Built-in validators:
import { validators } from '@mycuppa/contact-form'
const fields = [
{
name: 'email',
type: 'email',
label: 'Email',
validation: validators.email,
},
{
name: 'phone',
type: 'tel',
label: 'Phone',
validation: validators.phone,
},
{
name: 'website',
type: 'text',
label: 'Website',
validation: validators.url,
},
]
Custom validation:
const fields = [
{
name: 'code',
type: 'text',
label: 'Promo Code',
validation: (value) => {
if (value && value.length !== 8) {
return 'Code must be 8 characters'
}
},
},
]
Backend Example (Next.js)
// app/api/contact/route.ts
import { NextRequest, NextResponse } from 'next/server'
import { sendEmail } from '@/lib/email'
export async function POST(request: NextRequest) {
const data = await request.json()
// Validate
if (!data.name || !data.email || !data.message) {
return NextResponse.json(
{ error: 'Missing required fields' },
{ status: 400 }
)
}
// Send email
await sendEmail({
to: 'support@example.com',
from: data.email,
subject: `Contact Form: ${data.name}`,
html: `
<h2>New Contact Form Submission</h2>
<p><strong>Name:</strong> ${data.name}</p>
<p><strong>Email:</strong> ${data.email}</p>
<p><strong>Message:</strong></p>
<p>${data.message}</p>
`,
})
return NextResponse.json({ success: true })
}
Styling
<ContactForm
className="contact-form"
style={{
'--input-border': '#e5e7eb',
'--input-focus': '#3b82f6',
'--button-bg': '#3b82f6',
}}
/>
Related
- Button - Submit button customization
- Input - Individual form inputs
- Map - Add location selector
- Contact Us Screen - Full contact page