import { useForm, SubmitHandler } from 'react-hook-form';
import { useState } from 'react';
import './styles.css';
interface FormData {
firstName: string;
lastName: string;
email: string;
age: number;
password: string;
confirmPassword: string;
gender: string;
newsletter: boolean;
}
export default function App() {
const [submittedData, setSubmittedData] = useState<FormData | null>(null);
const {
register,
handleSubmit,
watch,
reset,
formState: { errors, isSubmitting, isValid, dirtyFields }
} = useForm<FormData>({
mode: 'onChange',
defaultValues: {
firstName: '',
lastName: '',
email: '',
age: undefined,
password: '',
confirmPassword: '',
gender: '',
newsletter: false
}
});
const password = watch('password');
const watchAllFields = watch();
const onSubmit: SubmitHandler<FormData> = async (data) => {
await new Promise(resolve => setTimeout(resolve, 1000));
setSubmittedData(data);
};
const handleReset = () => {
reset();
setSubmittedData(null);
};
return (
<div className="container">
<h1>๐ React Hook Form Demo</h1>
<p className="subtitle">Performant, flexible and extensible forms with easy-to-use validation</p>
<div className="content">
<form onSubmit={handleSubmit(onSubmit)} className="form">
<div className="form-row">
<div className="form-group">
<label>First Name</label>
<input
{...register('firstName', {
required: 'First name is required',
minLength: { value: 2, message: 'Min 2 characters' }
})}
placeholder="John"
className={errors.firstName ? 'error' : dirtyFields.firstName ? 'valid' : ''}
/>
{errors.firstName && <span className="error-msg">{errors.firstName.message}</span>}
</div>
<div className="form-group">
<label>Last Name</label>
<input
{...register('lastName', {
required: 'Last name is required'
})}
placeholder="Doe"
className={errors.lastName ? 'error' : dirtyFields.lastName ? 'valid' : ''}
/>
{errors.lastName && <span className="error-msg">{errors.lastName.message}</span>}
</div>
</div>
<div className="form-group">
<label>Email</label>
<input
type="email"
{...register('email', {
required: 'Email is required',
pattern: {
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
message: 'Invalid email address'
}
})}
placeholder="john@example.com"
className={errors.email ? 'error' : dirtyFields.email ? 'valid' : ''}
/>
{errors.email && <span className="error-msg">{errors.email.message}</span>}
</div>
<div className="form-group">
<label>Age</label>
<input
type="number"
{...register('age', {
required: 'Age is required',
min: { value: 18, message: 'Must be at least 18' },
max: { value: 99, message: 'Must be under 100' }
})}
placeholder="25"
className={errors.age ? 'error' : dirtyFields.age ? 'valid' : ''}
/>
{errors.age && <span className="error-msg">{errors.age.message}</span>}
</div>
<div className="form-row">
<div className="form-group">
<label>Password</label>
<input
type="password"
{...register('password', {
required: 'Password is required',
minLength: { value: 6, message: 'Min 6 characters' }
})}
placeholder="โขโขโขโขโขโข"
className={errors.password ? 'error' : dirtyFields.password ? 'valid' : ''}
/>
{errors.password && <span className="error-msg">{errors.password.message}</span>}
</div>
<div className="form-group">
<label>Confirm Password</label>
<input
type="password"
{...register('confirmPassword', {
required: 'Please confirm password',
validate: value => value === password || 'Passwords do not match'
})}
placeholder="โขโขโขโขโขโข"
className={errors.confirmPassword ? 'error' : dirtyFields.confirmPassword ? 'valid' : ''}
/>
{errors.confirmPassword && <span className="error-msg">{errors.confirmPassword.message}</span>}
</div>
</div>
<div className="form-group">
<label>Gender</label>
<select
{...register('gender', { required: 'Please select gender' })}
className={errors.gender ? 'error' : dirtyFields.gender ? 'valid' : ''}
>
<option value="">Select...</option>
<option value="male">Male</option>
<option value="female">Female</option>
<option value="other">Other</option>
</select>
{errors.gender && <span className="error-msg">{errors.gender.message}</span>}
</div>
<div className="form-group checkbox-group">
<label className="checkbox-label">
<input
type="checkbox"
{...register('newsletter')}
/>
<span>Subscribe to newsletter</span>
</label>
</div>
<div className="button-group">
<button type="submit" disabled={isSubmitting} className="submit-btn">
{isSubmitting ? 'โณ Submitting...' : '๐ Submit'}
</button>
<button type="button" onClick={handleReset} className="reset-btn">
๐ Reset
</button>
</div>
</form>
<div className="sidebar">
<div className="watch-panel">
<h3>๐ Live Watch</h3>
<pre>{JSON.stringify(watchAllFields, null, 2)}</pre>
</div>
<div className="status-panel">
<h3>๐ Form Status</h3>
<div className="status-item">
<span>Valid:</span>
<span className={isValid ? 'status-yes' : 'status-no'}>
{isValid ? 'โ
Yes' : 'โ No'}
</span>
</div>
<div className="status-item">
<span>Errors:</span>
<span>{Object.keys(errors).length}</span>
</div>
<div className="status-item">
<span>Dirty Fields:</span>
<span>{Object.keys(dirtyFields).length}</span>
</div>
</div>
{submittedData && (
<div className="result-panel">
<h3>โ
Submitted Data</h3>
<pre>{JSON.stringify(submittedData, null, 2)}</pre>
</div>
)}
</div>
</div>
</div>
);
}