ReactForm
Home/Tools/Text Input

React Form with Text Input

Text inputs are the backbone of nearly every React form, but even something this simple creates boilerplate fast. A shared onChange handler using computed property names eliminates per-field handlers and keeps your state management concise. Getting this pattern right from the start means your form scales cleanly as fields are added.

ReactForm Team·May 2026·3 min read

The challenge

Managing text inputs in React forms often leads to repetitive JSX and state handling for each field.

  • Writing a separate onChange handler for every field causes code duplication
  • Tracking which fields are empty for required validation without a library is verbose
  • Resetting form state after submission requires touching every field individually
  • Keeping the input value controlled and the UI in sync when the user types quickly

Working code example

Here is a complete, self-contained working example you can drop directly into any React project. It uses Tailwind CSS for styling and requires no external dependencies beyond React itself.

TextInputForm.jsx
import { useState } from 'react';

export default function TextInputForm() {
  const [form, setForm] = useState({ name: '', email: '', phone: '' });
  const [errors, setErrors] = useState({});
  const [submitted, setSubmitted] = useState(false);

  const handle = (e) =>
    setForm({ ...form, [e.target.name]: e.target.value });

  const validate = () => {
    const errs = {};
    if (!form.name.trim()) errs.name = 'Name is required';
    if (!form.email.trim()) errs.email = 'Email is required';
    else if (!/\S+@\S+\.\S+/.test(form.email)) errs.email = 'Enter a valid email';
    if (!form.phone.trim()) errs.phone = 'Phone is required';
    return errs;
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    const errs = validate();
    if (Object.keys(errs).length) { setErrors(errs); return; }
    setSubmitted(true);
  };

  if (submitted)
    return <p className="p-6 text-green-600 font-medium">Form submitted successfully!</p>;

  return (
    <form onSubmit={handleSubmit} className="max-w-md mx-auto p-6 space-y-4 bg-white rounded-2xl shadow">
      <h2 className="text-xl font-bold text-gray-800">Contact Details</h2>
      {[
        { name: 'name', label: 'Full Name', type: 'text' },
        { name: 'email', label: 'Email Address', type: 'email' },
        { name: 'phone', label: 'Phone Number', type: 'tel' },
      ].map(({ name, label, type }) => (
        <div key={name}>
          <label className="block text-sm font-medium text-gray-700 mb-1">{label}</label>
          <input
            type={type}
            name={name}
            value={form[name]}
            onChange={handle}
            className={`w-full border rounded-lg px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-teal-500 ${errors[name] ? 'border-red-400' : 'border-gray-300'}`}
          />
          {errors[name] && <p className="text-red-500 text-xs mt-1">{errors[name]}</p>}
        </div>
      ))}
      <button type="submit" className="w-full bg-teal-600 text-white font-semibold py-2 rounded-lg hover:bg-teal-700 transition">
        Submit
      </button>
    </form>
  );
}

How ReactForm.co helps

ReactForm's visual builder handles all of the above — text input configuration, validation rules, state management, and responsive layout — without writing a single line of code. Drag fields onto the canvas, configure their properties in the sidebar, and get production-ready React output. You can publish the form and collect responses instantly, or export the JSX to drop into your own codebase.

Build this form visually

Frequently asked questions

What is a controlled input in React?

A controlled input is one where React state is the single source of truth for the input value. You pass value={state} and onChange to the input, which means React always knows what the user has typed. This is the standard approach because it lets you validate, transform, and reset input values programmatically.

How do I handle multiple text inputs without writing a handler for each one?

Use the name attribute on each input and a single shared handler: const handle = e => setForm({ ...form, [e.target.name]: e.target.value }). The computed property name [e.target.name] maps each event to the correct field in state. This pattern scales to any number of fields with no extra code.

Should I use uncontrolled inputs with useRef instead of useState?

Uncontrolled inputs with useRef are fine for simple cases where you only need the value on submit. However, controlled inputs are easier to validate in real time, easier to reset, and easier to prefill with existing data. Use uncontrolled inputs only when performance is a genuine concern with very large forms.

Related topics

Build this form visually — no code needed

ReactForm.co handles text input fields, validation, conditional logic, and responsive layout automatically. Publish in minutes and collect responses for free.