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)