Simplifying Form Handling in React with FormData and TypeScript
💡 Handling forms in React doesn't have to be hard. Learn how FormData, combined with TypeScript and Zod, can simplify your workflow.
🔍 What is FormData?
FormData is a browser API that allows you to work directly with form data without managing states manually. For example:
function onSubmit(event: React.FormEvent) {
event.preventDefault();
const formData = new FormData(event.currentTarget);
const formValues = {
name: formData.get('name'),
email: formData.get('email'),
};
console.log(formValues); // { name: 'John', email: 'john@example.com' }
}
📝 Key Tip: Ensure every input has a matching name attribute for this to work.
<input type="text" name="name" />
<input type="email" name="email" />
🤔 Challenges with TypeScript
While FormData simplifies JavaScript form handling, TypeScript introduces complexity:
const quantity = formData.get('quantity');
// TypeScript infers this as `FormDataEntryValue | null`
This requires extra steps to ensure type safety:
const quantity = (formData.get('quantity') as string | null) || '0';
const parsedQuantity = parseInt(quantity, 10);
This works, but it’s tedious and error-prone for larger forms.
💡 Pro Tip: Use Object.fromEntries
To convert FormData into a plain object:
const formValues = Object.fromEntries(formData);
console.log(formValues); // { name: 'John', email: 'john@example.com' }
However, TypeScript still infers this as:
{ [key: string]: FormDataEntryValue }
✨ Solving Issues with Zod
Zod is a game-changer for validation and type safety.
Define a schema:
import { z } from 'zod';
const formSchema = z.object({
name: z.string().nonempty("Name is required"),
email: z.string().email("Invalid email"),
quantity: z.string().transform((val) => parseInt(val, 10)).optional(),
});
Validate and type-check:
const formValues = Object.fromEntries(formData);
const results = formSchema.safeParse(formValues);
if (results.success) {
console.log(results.data); // Typed data
} else {
console.error(results.error.issues); // Validation errors
}
✅ Benefits: No manual type coercion, robust validation, and type-safe results.
📈 Modern React and FormData
In React 19+, you can handle FormData with action directly:
function MyForm() {
function handleAction(formData: FormData) {
const formValues = Object.fromEntries(formData);
console.log(formValues);
}
return <form action={handleAction}>...</form>;
}
This aligns React with web standards and simplifies handling submissions.
🌐 Framework Support
Frameworks like Remix take this even further:
export async function action({ request }: { request: Request }) {
const formData = await request.formData();
console.log(formData.get('email')); // 'john@example.com'
}
This "Remix way" integrates perfectly with modern APIs.
🚀 Conclusion
FormData is a powerful tool for handling forms in React. Pairing it with TypeScript and Zod resolves its limitations, offering:
👉 Try it in your next project and see the difference! 💬 What’s your experience with FormData? Let’s discuss in the comments!