Building Forms in React — From Design to Working Component
Forms are one of the most common UI patterns and one of the trickiest to prototype. Here's how React handles them, and how to get AI to generate them correctly.

Forms Are Hard to Prototype
In Figma, a form is essentially cosmetic. You can design what it looks like, but you can't make it actually validate, submit, or respond to user input in a meaningful way. Prototyping form flows requires linking screens and making assumptions about what "success" looks like.
In React, forms can be fully functional. Validation, submission, state transitions, empty states — all of it works, in a prototype that can become production code.
A Sign-Up Form Example
Here's a complete, working sign-up form:
'use client';
import { useState } from 'react';
export default function SignUpForm() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [role, setRole] = useState('');
const [submitted, setSubmitted] = useState(false);
function handleSubmit(e) {
e.preventDefault();
setSubmitted(true);
}
if (submitted) {
return (
<div className="p-8 text-center">
<h2 className="text-2xl font-bold mb-2">You're in!</h2>
<p className="text-gray-600">Welcome, {name}. We'll be in touch at {email}.</p>
</div>
);
}
return (
<form onSubmit={handleSubmit} className="space-y-4 p-8 max-w-md">
<input
type="text"
placeholder="Your name"
value={name}
onChange={(e) => setName(e.target.value)}
required
className="w-full px-4 py-3 rounded-xl border border-gray-200"
/>
<input
type="email"
placeholder="Email address"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
className="w-full px-4 py-3 rounded-xl border border-gray-200"
/>
<select
value={role}
onChange={(e) => setRole(e.target.value)}
className="w-full px-4 py-3 rounded-xl border border-gray-200"
>
<option value="">Select your role</option>
<option value="designer">Designer</option>
<option value="pm">Product Manager</option>
<option value="engineer">Engineer</option>
</select>
<button type="submit" className="w-full py-3 bg-indigo-600 text-white rounded-xl font-semibold">
Sign up
</button>
</form>
);
}The Key Patterns
Controlled Inputs
value={name}
onChange={(e) => setName(e.target.value)}A controlled input is one where React owns the value. Every keystroke calls setName, which updates the state, which updates the input's displayed value. This keeps the UI in sync with your data.
e.preventDefault()
function handleSubmit(e) {
e.preventDefault();
// ...
}By default, submitting an HTML form reloads the page. e.preventDefault() stops that. This is always the first line of a form submit handler in React.
View Switching
if (submitted) {
return <SuccessView />;
}
return <FormView />;Rather than navigating to a new page, you can switch what's rendered based on state. This is how you prototype success states, empty states, and loading states — all in a single component.
HTML Validation
required
type="email"Adding required to an input means the form won't submit unless that field is filled in. type="email" makes the browser validate email format automatically. These built-in platform behaviours give you real form validation for free.
Getting AI to Generate Forms Correctly
When prompting AI for a form, be specific:
- "Build a sign-up form with name, email, and a role dropdown"
- "Add a success state that shows a confirmation message after submission"
- "Use controlled inputs with useState for each field"
- "Add HTML validation — required fields and email type"
The more precise your prompt, the less cleanup you'll need to do. Forms are one of the areas where AI can go in many directions, so guiding it to specific patterns saves time.
Why This Matters
A form that actually works — that validates, that shows a success state, that handles empty inputs gracefully — is the difference between a prototype that communicates intent and one that reveals it. Stakeholders respond differently to a working form than a static screen.
With React, you can build the former as quickly as the latter.