Forms are a fundamental part of any application. By combining React Hook Form and Tailwind CSS, you can build accessible, responsive, and well-validated forms with minimal code and maximum flexibility.
Why React Hook Form?
- Small and performant
- Easy to integrate with UI libraries
- Built-in validation
- Supports schema-based validation with Zod/Yup
Install Dependencies
npm install react-hook-form
ContactForm.js
import { useForm } from 'react-hook-form'
export default function ContactForm() {
const {
register,
handleSubmit,
formState: { errors, isSubmitting },
} = useForm()
const onSubmit = (data) => {
console.log(data)
}
return (
<form
onSubmit={handleSubmit(onSubmit)}
className="max-w-md mx-auto bg-white p-6 shadow rounded space-y-4"
>
<div>
<label className="block text-sm font-medium text-gray-700">
Name
</label>
<input
{...register('name', { required: 'Name is required' })}
className={`mt-1 block w-full border rounded px-3 py-2 ${
errors.name
? 'border-red-500 focus:border-red-500'
: 'border-gray-300 focus:border-indigo-500'
}`}
/>
{errors.name && (
<p className="text-red-600 text-sm mt-1">{errors.name.message}</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-gray-700">
Email
</label>
<input
{...register('email', {
required: 'Email is required',
pattern: {
value: /^\S+@\S+$/i,
message: 'Invalid email address',
},
})}
className={`mt-1 block w-full border rounded px-3 py-2 ${
errors.email
? 'border-red-500 focus:border-red-500'
: 'border-gray-300 focus:border-indigo-500'
}`}
/>
{errors.email && (
<p className="text-red-600 text-sm mt-1">{errors.email.message}</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-gray-700">
Message
</label>
<textarea
{...register('message', { required: 'Message is required' })}
className={`mt-1 block w-full border rounded px-3 py-2 h-24 ${
errors.message
? 'border-red-500 focus:border-red-500'
: 'border-gray-300 focus:border-indigo-500'
}`}
/>
{errors.message && (
<p className="text-red-600 text-sm mt-1">{errors.message.message}</p>
)}
</div>
<button
type="submit"
disabled={isSubmitting}
className="w-full bg-indigo-600 text-white py-2 px-4 rounded hover:bg-indigo-700 transition"
>
{isSubmitting ? 'Sending...' : 'Send Message'}
</button>
</form>
)
}
Key Tailwind Utilities Used
-
border
,rounded
,focus:border-*
: Interactive styling -
text-sm
,mt-1
,space-y-4
: Typography and spacing -
bg-indigo-600 hover:bg-indigo-700
: Styled button -
text-red-600
: Validation error display
Bonus: Schema Validation with Zod
Install:
npm install zod @hookform/resolvers
Add validation:
import { z } from 'zod'
import { zodResolver } from '@hookform/resolvers/zod'
const schema = z.object({
name: z.string().min(1, 'Name is required'),
email: z.string().email('Invalid email'),
message: z.string().min(1, 'Message is required'),
})
const {
register,
handleSubmit,
formState: { errors },
} = useForm({
resolver: zodResolver(schema),
})
Final Thoughts
React Hook Form keeps forms lean, while Tailwind gives you all the control for layout and interaction. Together, they help you build fast, user-friendly forms without custom CSS or complex logic.
Mastering Tailwind at Scale: Architecture, Patterns & Performance
Build smarter, validate simpler.
Top comments (0)