← Back to blog

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.

Amarneethi·
Building Forms in React — From Design to Working Component

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:

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.