<input>

Renders a browser form control that React can drive from state or leave to the DOM.

Signature
<input name="myInput" />

Parameters

ParameterTypeDescription
$valueoptionalstringFor a text input, controls its text. For a radio button, specifies its form data. Passing it makes the input controlled and requires an onChange handler.
$checkedoptionalbooleanFor a checkbox or radio button, controls whether it is selected. Passing it makes the input controlled and requires an onChange handler.
$defaultValueoptionalstringSpecifies the initial value for a text input in an uncontrolled input.
$defaultCheckedoptionalbooleanSpecifies the initial value for type="checkbox" and type="radio" in an uncontrolled input.
$onChangeoptional(event: Event) => voidRequired for controlled inputs. Fires immediately when the value is changed by the user — on every keystroke. Behaves like the browser input event, not the native change event.
$typeoptionalstring= 'text'One of the HTML input types: text, checkbox, radio, file, number, date, submit, image and so on.
$nameoptionalstringThe name this input is submitted under in the form data. Fields without a name never appear in FormData.
$formActionoptionalstring | ((formData: FormData) => void | Promise<void>)Overrides the parent <form action> for type="submit" and type="image". A URL behaves like a standard HTML form; a function handles the submission.
$disabledoptionalboolean= falseIf true, the input will not be interactive and will appear dimmed.
$readOnlyoptionalboolean= falseIf true, the input is not editable by the user but still submits its value.
$requiredoptionalboolean= falseIf true, the value must be provided for the form to submit.
$...propsoptionalHTMLInputElement props<input> supports all common element props plus accept, alt, autoComplete, autoFocus, capture, dirname, form, formEnctype, formMethod, formNoValidate, formTarget, height, list, max, maxLength, min, minLength, multiple, pattern, placeholder, size, src, step, width and the onInput, onInvalid and onSelect handlers. It does not accept children.

Return value

ReactElementRenders a browser <input> element. Its value is owned by React when value or checked is passed, and by the DOM otherwise.

Errors

React logs an error when an input switches between controlled and uncontrolled during its lifetime, or when a value prop is passed without an onChange handler and without readOnly.

Changelog

  • PHP 19.0.0formAction accepts a function, letting a submit input override the parent form's action.
  • PHP 19.0.0ref is an ordinary prop on function components, so wrapper components around <input> no longer need forwardRef.

<input> is the built-in browser control, and the single decision that shapes every bug around it is who owns the value. Pass value (or checked for checkboxes and radios) and React owns it: the DOM will only ever display what state says, so an onChange that writes that state back is mandatory. Pass defaultValue (or defaultChecked) and the DOM owns it; you read it later through a ref or from the form's FormData.

Choosing between them

Control the input when something else on screen depends on the current text — a live preview, a disabled submit button, a filter. Leave it uncontrolled for ordinary fields you only read once, on submit; it is less code and fewer renders.

What trips people up

  • Checkboxes and radios use checked/defaultChecked, never value. Read event.target.checked, not event.target.value.
  • value={undefined} or value={null} makes an input uncontrolled, producing the famous "changing an uncontrolled input to be controlled" warning. Initialise state to ''.
  • React's onChange fires on every keystroke, unlike the native change event that fires on blur.
  • type="file" can only ever be uncontrolled.

Example

Try it yourself
Loading editor…
Loading React…

When to use it

  • Filter a list as the user types by controlling the search field with state
  • Collect a signup form's fields uncontrolled and read them all from FormData on submit
  • Toggle a settings checkbox and immediately enable or disable dependent controls

Frequently asked questions

Why does React warn that a component is changing an uncontrolled input to be controlled?

The input's value prop was undefined or null on the first render and became a string later. Initialise the state with an empty string — useState('') rather than useState() or useState(null) — or pass value={data.name ?? ''} when the value comes from data that arrives asynchronously.

Why can't I type in my input?

You passed a value prop without an onChange handler that writes the new value back to state, so React re-renders the field with the same old value after every keystroke. Add onChange={e => setValue(e.target.value)}, or use defaultValue if the field is meant to be uncontrolled, or add readOnly if it is genuinely fixed.

How do I get the value of an input on submit?

Either read it from state if the input is controlled, or build a FormData from the form element — new FormData(event.currentTarget) — and call data.get('fieldName'). With a function form action, React hands you that FormData directly. Every field you want to read needs a name prop.

More examples

Reading every field at once with FormData

Submitting renders `street=12 Baker St, city=London, gift=on, note=`. Three details are worth noting: an unchecked checkbox is absent from FormData entirely rather than present as false, a checked one contributes the string "on" unless you give it a value prop, and an empty text field still appears with an empty string. No state is needed for the fields themselves — only for showing the result.

Example · jsx
import { useState } from "react";

export default function ShippingForm() {
  const [summary, setSummary] = useState("not submitted");

  function handleSubmit(e) {
    e.preventDefault();
    const data = new FormData(e.currentTarget);
    const entries = [...data.entries()]
      .map(([key, value]) => `${key}=${value}`)
      .join(", ");
    setSummary(entries);
  }

  return (
    <form onSubmit={handleSubmit}>
      <input name="street" defaultValue="12 Baker St" />
      <input name="city" defaultValue="London" />
      <input type="checkbox" name="gift" defaultChecked />
      <input name="note" />
      <button type="submit">Submit</button>
      <p>{summary}</p>
    </form>
  );
}

A file input is always uncontrolled

Picking two images renders their names as a sorted list. A file input cannot be controlled — the browser refuses to let script set its value to anything but the empty string, for security — so there is no value prop here at all. Clearing therefore means reaching for the DOM node through a ref and assigning `""`, which is the one legal assignment, then resetting the state that mirrors it.

Example · jsx
import { useRef, useState } from "react";

export default function FilePicker() {
  const inputRef = useRef(null);
  const [names, setNames] = useState([]);

  function handleChange(e) {
    const picked = Array.from(e.target.files).map((file) => file.name);
    setNames(picked.sort());
  }

  return (
    <div>
      <input
        type="file"
        ref={inputRef}
        multiple
        accept=".png,.jpg"
        onChange={handleChange}
      />
      <button onClick={() => { inputRef.current.value = ""; setNames([]); }}>
        Clear
      </button>
      <ul>
        {names.map((name) => (
          <li key={name}>{name}</li>
        ))}
      </ul>
    </div>
  );
}

A number field that survives an empty box

Typing 4 shows `Total: 20 credits`; clearing the box shows `Total: 0 credits` and leaves the field empty rather than snapping to 0. Storing Number(e.target.value) directly is the common mistake: an empty box parses to NaN, and coercing NaN back to a value prop makes React re-render the field as the string "NaN" that the user cannot delete. Keeping the raw string in state and deriving the number solves it.

Example · jsx
import { useState } from "react";

export default function QuantityField() {
  // Keep the raw string in state, not a number.
  const [raw, setRaw] = useState("3");
  const quantity = raw === "" ? 0 : Number(raw);

  return (
    <p>
      <label>
        Quantity:{" "}
        <input
          type="number"
          min={0}
          max={99}
          step={1}
          value={raw}
          onChange={(e) => setRaw(e.target.value)}
        />
      </label>{" "}
      Total: {quantity * 5} credits
    </p>
  );
}

Discussion

  • Be the first to comment on this lesson.