Contact Form

intermediate
30 min

Explorer

1
⚛️
ContactForm.jsx

Click files to view code →

React Contact Form Tutorial

1. What is the Question and What to Achieve Here

Create a contact form component that allows users to send messages. The form should include name, email, subject, and message fields with validation, submission handling, and success/error feedback.

[!TIP] Goal

  • Collect user contact information
  • Validate all fields before submission
  • Handle form submission (send to API/email service)
  • Show success/error messages
  • Reset form after successful submission
  • Make it accessible and user-friendly

2. How to Solve

Use React's

text
useState
hook to manage form state and validation errors. Implement field validation and handle form submission with async operations. Display appropriate feedback based on submission results.

3. What Are the Things That Need to Be Gathered

[!IMPORTANT] Requirements

  • React with hooks
  • Understanding of form handling and validation
  • API integration (optional: EmailJS, Formspree, or custom backend)
  • Error handling and user feedback
  • Controlled components

4. Key Topics to Consider and Plan of Action

Key Topics:

  • Form validation (name, email, message)
  • Textarea handling for long messages
  • Character limits for message field
  • API integration for sending emails
  • Loading and success states
  • Error handling

Plan of Action:

  1. Create ContactForm component
  2. Set up form state and validation
  3. Build form UI with all fields
  4. Implement validation logic
  5. Handle form submission
  6. Integrate with email service (or API)
  7. Display success/error feedback
  8. Add loading states

5. Code the Question

jsxShowing 50 of 291 lines
import React, { useState } from 'react';
import './ContactForm.css';

function ContactForm() {
  const [formData, setFormData] = useState({
    name: '',
    email: '',
    subject: '',
    message: ''
  });

  const [errors, setErrors] = useState({});
  const [isSubmitting, setIsSubmitting] = useState(false);
  const [submitStatus, setSubmitStatus] = useState(null); // 'success' or 'error'

  // Validation functions
  const validateName = (name) => {
    if (!name.trim()) return 'Name is required';
    if (name.trim().length < 2) return 'Name must be at least 2 characters';
    return '';
  };

  const validateEmail = (email) => {
    if (!email.trim()) return 'Email is required';
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (!emailRegex.test(email)) return 'Please enter a valid email';
    return '';
  };

  const validateSubject = (subject) => {
    if (!subject.trim()) return 'Subject is required';
    if (subject.trim().length < 3) return 'Subject must be at least 3 characters';
    return '';
  };

  const validateMessage = (message) => {
    if (!message.trim()) return 'Message is required';
    if (message.trim().length < 10) return 'Message must be at least 10 characters';
    if (message.length > 1000) return 'Message must be less than 1000 characters';
    return '';
  };

  // Handle input changes
  const handleChange = (e) => {
    const { name, value } = e.target;
    setFormData(prev => ({
      ...prev,
      [name]: value
    }));

// ...

CSS (ContactForm.css):

cssShowing 50 of 157 lines
.contact-form-container {
  max-width: 600px;
  margin: 2rem auto;
  padding: 2rem;
  background: #fff;
  border-radius: 12px;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
  font-family: Arial, sans-serif;
}

.contact-form-header {
  text-align: center;
  margin-bottom: 2rem;
}

.contact-form-header h2 {
  margin: 0 0 0.5rem 0;
  color: #333;
}

.contact-form-header p {
  margin: 0;
  color: #666;
  font-size: 0.95rem;
}

.alert {
  padding: 1rem;
  border-radius: 6px;
  margin-bottom: 1.5rem;
  font-weight: 500;
}

.alert-success {
  background-color: #e8f5e9;
  color: #2e7d32;
  border-left: 4px solid #4caf50;
}

.alert-error {
  background-color: #ffebee;
  color: #c62828;
  border-left: 4px solid #f44336;
}

.contact-form {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}
// ...

6. Things to Consider

Best Practices:

  • Validate all fields before submission
  • Provide clear, helpful error messages
  • Show character limits for text areas
  • Disable form during submission
  • Clear form after success
  • Handle API errors gracefully
  • Use proper ARIA attributes

Email Service Integration Examples:

javascript
// Using EmailJS
import emailjs from '@emailjs/browser';

const handleSubmit = async (e) => {
  e.preventDefault();
  if (!validateForm()) return;

  setIsSubmitting(true);
  
  try {
    await emailjs.send(
      'YOUR_SERVICE_ID',
      'YOUR_TEMPLATE_ID',
      formData,
      'YOUR_PUBLIC_KEY'
    );
    setSubmitStatus('success');
  } catch (error) {
    setSubmitStatus('error');
  } finally {
    setIsSubmitting(false);
  }
};

// Using Formspree
const handleSubmit = async (e) => {
  e.preventDefault();
  if (!validateForm()) return;

  setIsSubmitting(true);
  
  try {
    const response = await fetch('https://formspree.io/f/YOUR_FORM_ID', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(formData)
    });
    
    if (response.ok) {
      setSubmitStatus('success');
    } else {
      throw new Error('Failed');
    }
  } catch (error) {
    setSubmitStatus('error');
  } finally {
    setIsSubmitting(false);
  }
};

Testing:

  • Test all validation rules
  • Test successful submission
  • Test error handling
  • Test with disabled JavaScript
  • Test accessibility

Real-World Use Cases:

  • Customer support forms
  • Business inquiries
  • Feedback forms
  • Report issues
  • General contact

This contact form is production-ready and can be integrated with any email service or backend API.

No files open

Click a file in the explorer to view