React Form Examples
Modern React components with hooks, validation, and dynamic features.
Simple Contact Form
Basic React contact form using hooks for state management.
import React, { useState } from 'react';
function ContactForm() {
const [formData, setFormData] = useState({
name: '',
email: '',
message: ''
});
const [isSubmitting, setIsSubmitting] = useState(false);
const [submitStatus, setSubmitStatus] = useState('');
const handleChange = (e) => {
setFormData({
...formData,
[e.target.name]: e.target.value
});
};
const handleSubmit = async (e) => {
e.preventDefault();
setIsSubmitting(true);
setSubmitStatus('');
try {
const response = await fetch('https://connect.kitoform.com/f/YOUR_ENDPOINT_ID', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData),
});
if (response.ok) {
setSubmitStatus('success');
setFormData({ name: '', email: '', message: '' });
} else {
setSubmitStatus('error');
}
} catch (error) {
setSubmitStatus('error');
} finally {
setIsSubmitting(false);
}
};
return (
<form onSubmit={handleSubmit} className="max-w-md mx-auto">
<div className="mb-4">
<label htmlFor="name" className="block text-sm font-medium mb-2">
Name
</label>
<input
type="text"
name="name"
id="name"
value={formData.name}
onChange={handleChange}
required
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
</div>
<div className="mb-4">
<label htmlFor="email" className="block text-sm font-medium mb-2">
Email
</label>
<input
type="email"
name="email"
id="email"
value={formData.email}
onChange={handleChange}
required
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
</div>
<div className="mb-6">
<label htmlFor="message" className="block text-sm font-medium mb-2">
Message
</label>
<textarea
name="message"
id="message"
rows={4}
value={formData.message}
onChange={handleChange}
required
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
</div>
<button
type="submit"
disabled={isSubmitting}
className="w-full bg-blue-500 text-white py-2 px-4 rounded-md hover:bg-blue-600 disabled:opacity-50"
>
{isSubmitting ? 'Sending...' : 'Send Message'}
</button>
{submitStatus === 'success' && (
<p className="mt-4 text-green-600">Message sent successfully!</p>
)}
{submitStatus === 'error' && (
<p className="mt-4 text-red-600">Error sending message. Please try again.</p>
)}
</form>
);
}
export default ContactForm;Form with Validation
React form with client-side validation and error handling.
import React, { useState } from 'react';
function ValidatedForm() {
const [formData, setFormData] = useState({
firstName: '',
lastName: '',
email: '',
phone: '',
company: ''
});
const [errors, setErrors] = useState({});
const [isSubmitting, setIsSubmitting] = useState(false);
const validateForm = () => {
const newErrors = {};
if (!formData.firstName.trim()) {
newErrors.firstName = 'First name is required';
}
if (!formData.lastName.trim()) {
newErrors.lastName = 'Last name is required';
}
if (!formData.email.trim()) {
newErrors.email = 'Email is required';
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) {
newErrors.email = 'Please enter a valid email';
}
if (formData.phone && !/^[\+]?[0-9\s\-\(\)]+$/.test(formData.phone)) {
newErrors.phone = 'Please enter a valid phone number';
}
if (!formData.company.trim()) {
newErrors.company = 'Company name is required';
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const handleChange = (e) => {
const { name, value } = e.target;
setFormData(prev => ({
...prev,
[name]: value
}));
// Clear error when user starts typing
if (errors[name]) {
setErrors(prev => ({
...prev,
[name]: ''
}));
}
};
const handleSubmit = async (e) => {
e.preventDefault();
if (!validateForm()) {
return;
}
setIsSubmitting(true);
try {
const response = await fetch('https://connect.kitoform.com/f/YOUR_ENDPOINT_ID', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
...formData,
form_type: 'business_inquiry'
}),
});
if (response.ok) {
alert('Form submitted successfully!');
setFormData({
firstName: '',
lastName: '',
email: '',
phone: '',
company: ''
});
}
} catch (error) {
alert('Error submitting form. Please try again.');
} finally {
setIsSubmitting(false);
}
};
const inputClasses = "w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2";
const getInputClass = (fieldName) =>
`${inputClasses} ${errors[fieldName] ? 'border-red-500 focus:ring-red-500' : 'border-gray-300 focus:ring-blue-500'}`;
return (
<form onSubmit={handleSubmit} className="max-w-lg mx-auto">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
<div>
<label htmlFor="firstName" className="block text-sm font-medium mb-2">
First Name *
</label>
<input
type="text"
name="firstName"
id="firstName"
value={formData.firstName}
onChange={handleChange}
className={getInputClass('firstName')}
/>
{errors.firstName && (
<p className="mt-1 text-sm text-red-600">{errors.firstName}</p>
)}
</div>
<div>
<label htmlFor="lastName" className="block text-sm font-medium mb-2">
Last Name *
</label>
<input
type="text"
name="lastName"
id="lastName"
value={formData.lastName}
onChange={handleChange}
className={getInputClass('lastName')}
/>
{errors.lastName && (
<p className="mt-1 text-sm text-red-600">{errors.lastName}</p>
)}
</div>
</div>
<div className="mb-4">
<label htmlFor="email" className="block text-sm font-medium mb-2">
Email *
</label>
<input
type="email"
name="email"
id="email"
value={formData.email}
onChange={handleChange}
className={getInputClass('email')}
/>
{errors.email && (
<p className="mt-1 text-sm text-red-600">{errors.email}</p>
)}
</div>
<div className="mb-4">
<label htmlFor="phone" className="block text-sm font-medium mb-2">
Phone
</label>
<input
type="tel"
name="phone"
id="phone"
value={formData.phone}
onChange={handleChange}
className={getInputClass('phone')}
/>
{errors.phone && (
<p className="mt-1 text-sm text-red-600">{errors.phone}</p>
)}
</div>
<div className="mb-6">
<label htmlFor="company" className="block text-sm font-medium mb-2">
Company *
</label>
<input
type="text"
name="company"
id="company"
value={formData.company}
onChange={handleChange}
className={getInputClass('company')}
/>
{errors.company && (
<p className="mt-1 text-sm text-red-600">{errors.company}</p>
)}
</div>
<button
type="submit"
disabled={isSubmitting}
className="w-full bg-blue-500 text-white py-2 px-4 rounded-md hover:bg-blue-600 disabled:opacity-50 disabled:cursor-not-allowed"
>
{isSubmitting ? 'Submitting...' : 'Submit'}
</button>
</form>
);
}
export default ValidatedForm;Newsletter with Custom Hook
Reusable form logic with custom React hook for better code organization.
import React, { useState } from 'react';
// Custom hook for form handling
function useForm(initialState, onSubmit) {
const [values, setValues] = useState(initialState);
const [isSubmitting, setIsSubmitting] = useState(false);
const [submitStatus, setSubmitStatus] = useState(null);
const handleChange = (e) => {
const { name, value, type, checked } = e.target;
setValues(prev => ({
...prev,
[name]: type === 'checkbox' ? checked : value
}));
};
const handleSubmit = async (e) => {
e.preventDefault();
setIsSubmitting(true);
setSubmitStatus(null);
try {
await onSubmit(values);
setSubmitStatus('success');
setValues(initialState);
} catch (error) {
setSubmitStatus('error');
} finally {
setIsSubmitting(false);
}
};
const reset = () => {
setValues(initialState);
setSubmitStatus(null);
};
return {
values,
isSubmitting,
submitStatus,
handleChange,
handleSubmit,
reset
};
}
function NewsletterForm() {
const initialFormState = {
email: '',
firstName: '',
interests: [],
marketing_consent: false
};
const submitForm = async (formData) => {
const response = await fetch('https://connect.kitoform.com/f/YOUR_ENDPOINT_ID', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
...formData,
form_type: 'newsletter_signup',
interests: formData.interests.join(', ')
}),
});
if (!response.ok) {
throw new Error('Submission failed');
}
};
const {
values,
isSubmitting,
submitStatus,
handleChange,
handleSubmit
} = useForm(initialFormState, submitForm);
const handleInterestChange = (interest) => {
const currentInterests = values.interests;
const updatedInterests = currentInterests.includes(interest)
? currentInterests.filter(i => i !== interest)
: [...currentInterests, interest];
handleChange({
target: { name: 'interests', value: updatedInterests }
});
};
return (
<div className="max-w-md mx-auto">
<div className="mb-6 text-center">
<h3 className="text-xl font-semibold mb-2">Join Our Newsletter</h3>
<p className="text-gray-600">Stay updated with our latest news and updates</p>
</div>
<form onSubmit={handleSubmit}>
<div className="mb-4">
<label htmlFor="email" className="block text-sm font-medium mb-2">
Email Address *
</label>
<input
type="email"
name="email"
id="email"
value={values.email}
onChange={handleChange}
required
placeholder="Enter your email"
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
</div>
<div className="mb-4">
<label htmlFor="firstName" className="block text-sm font-medium mb-2">
First Name (Optional)
</label>
<input
type="text"
name="firstName"
id="firstName"
value={values.firstName}
onChange={handleChange}
placeholder="Your first name"
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
</div>
<div className="mb-4">
<label className="block text-sm font-medium mb-3">
What interests you? (Optional)
</label>
<div className="space-y-2">
{['Product Updates', 'Industry News', 'Tips & Tutorials', 'Events'].map((interest) => (
<label key={interest} className="flex items-center">
<input
type="checkbox"
checked={values.interests.includes(interest)}
onChange={() => handleInterestChange(interest)}
className="mr-2 h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
/>
<span className="text-sm">{interest}</span>
</label>
))}
</div>
</div>
<div className="mb-6">
<label className="flex items-start">
<input
type="checkbox"
name="marketing_consent"
checked={values.marketing_consent}
onChange={handleChange}
required
className="mr-2 h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded mt-0.5"
/>
<span className="text-sm text-gray-600">
I agree to receive marketing emails and understand I can unsubscribe at any time.
</span>
</label>
</div>
<button
type="submit"
disabled={isSubmitting || !values.marketing_consent}
className="w-full bg-blue-500 text-white py-2 px-4 rounded-md hover:bg-blue-600 disabled:opacity-50 disabled:cursor-not-allowed"
>
{isSubmitting ? 'Subscribing...' : 'Subscribe to Newsletter'}
</button>
</form>
{submitStatus === 'success' && (
<div className="mt-4 p-4 bg-green-50 border border-green-200 rounded-md">
<p className="text-green-800 text-sm">
✓ Successfully subscribed! Check your email for confirmation.
</p>
</div>
)}
{submitStatus === 'error' && (
<div className="mt-4 p-4 bg-red-50 border border-red-200 rounded-md">
<p className="text-red-800 text-sm">
✗ Error subscribing. Please try again.
</p>
</div>
)}
</div>
);
}
export default NewsletterForm;Dynamic Form Builder
Flexible form component that renders fields based on configuration.
import React, { useState } from 'react';
const FormField = ({ field, value, onChange, error }) => {
const baseClasses = "w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2";
const errorClasses = error
? "border-red-500 focus:ring-red-500"
: "border-gray-300 focus:ring-blue-500";
const renderField = () => {
switch (field.type) {
case 'text':
case 'email':
case 'tel':
return (
<input
type={field.type}
name={field.name}
value={value || ''}
onChange={(e) => onChange(field.name, e.target.value)}
placeholder={field.placeholder}
required={field.required}
className={`${baseClasses} ${errorClasses}`}
/>
);
case 'textarea':
return (
<textarea
name={field.name}
value={value || ''}
onChange={(e) => onChange(field.name, e.target.value)}
placeholder={field.placeholder}
required={field.required}
rows={field.rows || 3}
className={`${baseClasses} ${errorClasses}`}
/>
);
case 'select':
return (
<select
name={field.name}
value={value || ''}
onChange={(e) => onChange(field.name, e.target.value)}
required={field.required}
className={`${baseClasses} ${errorClasses}`}
>
<option value="">Select an option</option>
{field.options.map(option => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
);
case 'radio':
return (
<div className="space-y-2">
{field.options.map(option => (
<label key={option.value} className="flex items-center">
<input
type="radio"
name={field.name}
value={option.value}
checked={value === option.value}
onChange={(e) => onChange(field.name, e.target.value)}
className="mr-2 h-4 w-4 text-blue-600 focus:ring-blue-500"
/>
<span className="text-sm">{option.label}</span>
</label>
))}
</div>
);
case 'checkbox':
return (
<label className="flex items-start">
<input
type="checkbox"
name={field.name}
checked={value || false}
onChange={(e) => onChange(field.name, e.target.checked)}
required={field.required}
className="mr-2 h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded mt-0.5"
/>
<span className="text-sm">{field.label}</span>
</label>
);
default:
return null;
}
};
return (
<div className="mb-4">
{field.type !== 'checkbox' && (
<label className="block text-sm font-medium mb-2">
{field.label} {field.required && '*'}
</label>
)}
{renderField()}
{error && (
<p className="mt-1 text-sm text-red-600">{error}</p>
)}
</div>
);
};
function DynamicForm() {
// Form configuration
const formConfig = {
title: "Event Registration",
endpoint: "YOUR_ENDPOINT_ID",
fields: [
{
name: 'name',
type: 'text',
label: 'Full Name',
placeholder: 'Enter your full name',
required: true
},
{
name: 'email',
type: 'email',
label: 'Email Address',
placeholder: 'Enter your email',
required: true
},
{
name: 'event_type',
type: 'select',
label: 'Event Type',
required: true,
options: [
{ value: 'conference', label: 'Conference' },
{ value: 'workshop', label: 'Workshop' },
{ value: 'webinar', label: 'Webinar' }
]
},
{
name: 'experience_level',
type: 'radio',
label: 'Experience Level',
required: true,
options: [
{ value: 'beginner', label: 'Beginner' },
{ value: 'intermediate', label: 'Intermediate' },
{ value: 'advanced', label: 'Advanced' }
]
},
{
name: 'dietary_requirements',
type: 'textarea',
label: 'Dietary Requirements',
placeholder: 'Any dietary restrictions or allergies?',
rows: 3
},
{
name: 'terms',
type: 'checkbox',
label: 'I agree to the terms and conditions',
required: true
}
]
};
const [formData, setFormData] = useState({});
const [errors, setErrors] = useState({});
const [isSubmitting, setIsSubmitting] = useState(false);
const [submitStatus, setSubmitStatus] = useState(null);
const handleFieldChange = (fieldName, value) => {
setFormData(prev => ({
...prev,
[fieldName]: value
}));
// Clear error when user interacts with field
if (errors[fieldName]) {
setErrors(prev => ({
...prev,
[fieldName]: ''
}));
}
};
const validateForm = () => {
const newErrors = {};
formConfig.fields.forEach(field => {
if (field.required && !formData[field.name]) {
newErrors[field.name] = `${field.label} is required`;
}
});
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const handleSubmit = async (e) => {
e.preventDefault();
if (!validateForm()) return;
setIsSubmitting(true);
setSubmitStatus(null);
try {
const response = await fetch(`https://connect.kitoform.com/f/${formConfig.endpoint}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
...formData,
form_type: 'dynamic_form',
form_title: formConfig.title
}),
});
if (response.ok) {
setSubmitStatus('success');
setFormData({});
} else {
setSubmitStatus('error');
}
} catch (error) {
setSubmitStatus('error');
} finally {
setIsSubmitting(false);
}
};
return (
<div className="max-w-lg mx-auto">
<div className="mb-6 text-center">
<h3 className="text-xl font-semibold">{formConfig.title}</h3>
</div>
<form onSubmit={handleSubmit}>
{formConfig.fields.map((field) => (
<FormField
key={field.name}
field={field}
value={formData[field.name]}
onChange={handleFieldChange}
error={errors[field.name]}
/>
))}
<button
type="submit"
disabled={isSubmitting}
className="w-full bg-blue-500 text-white py-2 px-4 rounded-md hover:bg-blue-600 disabled:opacity-50"
>
{isSubmitting ? 'Submitting...' : 'Submit Registration'}
</button>
</form>
{submitStatus === 'success' && (
<div className="mt-4 p-4 bg-green-50 border border-green-200 rounded-md">
<p className="text-green-800 text-sm">Registration submitted successfully!</p>
</div>
)}
{submitStatus === 'error' && (
<div className="mt-4 p-4 bg-red-50 border border-red-200 rounded-md">
<p className="text-red-800 text-sm">Error submitting form. Please try again.</p>
</div>
)}
</div>
);
}
export default DynamicForm;