# React Bootstrap Form Controls

React Bootstrap form controls in AdminKit: inputs and textareas as plain Bootstrap markup in JSX, with sizes, disabled and read-only states, controlled or not.

Text inputs and textareas are the fields people type into: a name, an email address, a message. In AdminKit for React they are not components. You write Bootstrap's markup in JSX, with `className` instead of `class` and `htmlFor` instead of `for`. Bootstrap's Sass styles them in the free and PRO versions of [AdminKit for React](/react/), so every class of the [Bootstrap form docs](https://getbootstrap.com/docs/5.3/forms/form-control/) works.

Only three form fields have their own component, and they are part of AdminKit PRO: the [select](/react/docs/forms/select/), the [input mask](/react/docs/forms/input-mask/) and the [date picker](/react/docs/forms/date-picker/). The demo shows the other controls on the Basic Inputs page.

## Example

```html
<div class="mb-3">
  <label for="formControlEmail" class="form-label">Email address</label>
  <input type="email" class="form-control" id="formControlEmail" placeholder="name@example.com">
</div>
<div class="mb-3">
  <label for="formControlMessage" class="form-label">Message</label>
  <textarea class="form-control" id="formControlMessage" rows="3"></textarea>
</div>
```
```tsx code
<div className="mb-3">
  <label htmlFor="formControlEmail" className="form-label">Email address</label>
  <input type="email" className="form-control" id="formControlEmail" placeholder="name@example.com" />
</div>
<div className="mb-3">
  <label htmlFor="formControlMessage" className="form-label">Message</label>
  <textarea className="form-control" id="formControlMessage" rows={3} />
</div>
```

## Classes

| Class | Element | Description |
| --- | --- | --- |
| `form-label` | `<label>` | The label above a field |
| `form-control` | `<input>`, `<textarea>` | The text field |
| `form-control-lg`, `form-control-sm` | `.form-control` | Larger or smaller field |
| `form-control-plaintext` | `<input readOnly>` | A read-only value without the field border |
| `form-control-color` | `<input type="color">` | The color picker |
| `form-text` | `<div>` | Help text below a field |
| `form-select` | `<select>` | The native select, see [Select](/react/docs/forms/select/) |

## Controlled and uncontrolled inputs

An uncontrolled input keeps its own value. Give it a starting value with `defaultValue`, and read the value when the form is submitted. The demo pages work this way, and it is enough for most forms:

```tsx
import type { FormEvent } from "react";

export function ContactForm() {
  const onSubmit = (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    const data = new FormData(event.currentTarget);
    console.log(data.get("email"), data.get("message"));
  };

  return (
    <form onSubmit={onSubmit}>
      <div className="mb-3">
        <label htmlFor="contact-email" className="form-label">Email address</label>
        <input type="email" className="form-control" id="contact-email" name="email" required />
      </div>
      <div className="mb-3">
        <label htmlFor="contact-message" className="form-label">Message</label>
        <textarea className="form-control" id="contact-message" name="message" rows={3} />
      </div>
      <button type="submit" className="btn btn-primary">Send</button>
    </form>
  );
}
```

A controlled input takes its `value` from state and updates that state in `onChange`. Use one when the page reacts to each keystroke, such as a character count:

```tsx
import { useState } from "react";

export function BioField() {
  const [bio, setBio] = useState("");

  return (
    <div className="mb-3">
      <label htmlFor="bio" className="form-label">Bio</label>
      <textarea
        className="form-control"
        id="bio"
        rows={3}
        maxLength={160}
        value={bio}
        onChange={(event) => setBio(event.target.value)}
        aria-describedby="bio-count"
      />
      <div className="form-text" id="bio-count">{160 - bio.length} characters left</div>
    </div>
  );
}
```

A `value` without `onChange` makes the field read-only, and React warns about it. Use `defaultValue`, or add `readOnly` on purpose.

## Sizes

When a field sits next to a large button or in a compact toolbar, match its height with `form-control-lg` or `form-control-sm`.

```html
<input class="form-control form-control-lg mb-3" type="text" placeholder="Large input" aria-label="Large input">
<input class="form-control mb-3" type="text" placeholder="Medium input" aria-label="Medium input">
<input class="form-control form-control-sm" type="text" placeholder="Small input" aria-label="Small input">
```
```tsx code
<input className="form-control form-control-lg mb-3" type="text" placeholder="Large input" aria-label="Large input" />
<input className="form-control mb-3" type="text" placeholder="Medium input" aria-label="Medium input" />
<input className="form-control form-control-sm" type="text" placeholder="Small input" aria-label="Small input" />
```

## Disabled and read only

Use these when a field should show a value that the user cannot change. `disabled` grays the field out and leaves it out of the submitted form data. `readOnly` keeps the value selectable and submitted, but not editable.

```html
<input class="form-control mb-3" type="text" placeholder="Disabled input" aria-label="Disabled input" disabled>
<input class="form-control" type="text" placeholder="Readonly input" aria-label="Readonly input" readonly>
```
```tsx code
<input className="form-control mb-3" type="text" placeholder="Disabled input" aria-label="Disabled input" disabled />
<input className="form-control" type="text" placeholder="Readonly input" aria-label="Readonly input" readOnly />
```

## Accessibility

- Give every field a label: a `<label>` with `htmlFor` set to the field's `id`, or `aria-label` when the design has no visible label. A placeholder is not a label.
- Point `aria-describedby` at the `id` of help text (`.form-text`), so screen readers read it with the field.
- Use the right `type` (`email`, `tel`, `url`, `number`) and `autoComplete` values: mobile keyboards and password managers rely on them.
- A disabled field cannot get the focus. If the value has to stay readable by keyboard, use `readOnly`.

## Related

- [Next.js Bootstrap form controls](/nextjs/docs/forms/form-control/): the same form controls in AdminKit for Next.js
- [Bootstrap 5 form controls](/bootstrap/docs/forms/form-control/): the form controls of the HTML version
- [Form layout](/react/docs/forms/layout/): grids, horizontal forms and input groups
- [Validation](/react/docs/forms/validation/): feedback for invalid fields
- [Basic Inputs demo](https://react-demo.adminkit.io/forms-basic-inputs)

---

Canonical HTML page: https://adminkit.io/react/docs/forms/form-control/

Site overview for agents: https://adminkit.io/llms.txt (expanded: https://adminkit.io/llms-full.txt) · Documentation: https://adminkit.io/bootstrap/docs/ · Questions: support@adminkit.io
