Forms
|
This section documents Bootstrap 5.x as implemented by the official Bootstrap project. No specific patch version is pinned. Unlike the other reference sections on this site, no single reference book underpins it: the content was generated with the assistance of AI from general knowledge of Bootstrap, and should be verified against the current official documentation at getbootstrap.com/docs before relying on it in production. This section’s bibliography lists the reference material consulted while preparing these pages. |
Bootstrap does not replace the native HTML form elements described in
Form Accessibility & Validation — it styles them. Every
control below is still a plain <input>, <select>, or <textarea>; Bootstrap’s classes only change how it
looks and how it lines up with its label and neighboring controls. Everything on this page assumes the labels,
fieldset/legend grouping, and keyboard/focus concerns covered on that page still apply — Bootstrap adds
visual consistency, not accessibility, for free.
Form controls
Text inputs and textareas
The .form-control class is the base styling applied to any single-line or multi-line text control:
<div class="mb-3">
<label for="full-name" class="form-label">Full name</label>
<input type="text" class="form-control" id="full-name" placeholder="Jane Doe">
</div>
<div class="mb-3">
<label for="bio" class="form-label">Bio</label>
<textarea class="form-control" id="bio" rows="3"></textarea>
</div>
.form-control works with any <input> type that renders as a text-like box (text, email, password,
number, date, search, tel, url, and so on) — the browser-native behavior of that type (an email
keyboard on mobile, a native date picker) is untouched, only the border, padding, and focus ring are styled.
Sizing variants scale the control up or down without touching its surrounding layout:
<input type="text" class="form-control form-control-sm" placeholder="Small">
<input type="text" class="form-control" placeholder="Default">
<input type="text" class="form-control form-control-lg" placeholder="Large">
A control that should not accept input yet still look like a normal field uses readonly (a real value is
still submitted); one that should be visibly and functionally inert uses the disabled attribute:
<input type="text" class="form-control" value="Read-only value" readonly>
<input type="text" class="form-control" placeholder="Disabled" disabled>
Selects
.form-select styles a <select> with a custom dropdown indicator that matches the rest of the form controls:
<div class="mb-3">
<label for="country" class="form-label">Country</label>
<select class="form-select" id="country">
<option selected>Choose...</option>
<option value="es">Spain</option>
<option value="fr">France</option>
<option value="de">Germany</option>
</select>
</div>
<select class="form-select form-select-sm" aria-label="Small select">
<option selected>Small</option>
</select>
<select class="form-select form-select-lg" aria-label="Large select">
<option selected>Large</option>
</select>
A <select multiple> and a plain <select size="N"> (which renders as a scrollable list box rather than a
dropdown) both accept .form-select as well.
Checkboxes and radios
Bootstrap 5 restyles native checkboxes and radios rather than hiding them behind custom widgets, which keeps
keyboard operation and screen-reader semantics exactly as native as the underlying <input type="checkbox"> /
<input type="radio">. Each one is wrapped in a .form-check container:
<div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="subscribe">
<label class="form-check-label" for="subscribe">
Subscribe to the newsletter
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="plan" id="plan-basic" value="basic">
<label class="form-check-label" for="plan-basic">Basic plan</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="plan" id="plan-pro" value="pro" checked>
<label class="form-check-label" for="plan-pro">Pro plan</label>
</div>
Add .form-check-inline to lay a group of checks out on one line instead of stacked, and .form-switch to
render a checkbox as an on/off toggle switch instead of a square box:
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="inline-1" value="option1">
<label class="form-check-label" for="inline-1">1</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="inline-2" value="option2">
<label class="form-check-label" for="inline-2">2</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="dark-mode">
<label class="form-check-label" for="dark-mode">Dark mode</label>
</div>
Layout options
Stacked (default)
With no layout class beyond .mb-3 spacing on each field group, labels stack above their control and controls
stack vertically — this is the default seen in every example above, and the right choice for most forms.
Inline forms
Bootstrap 5 dropped the dedicated .form-inline class from Bootstrap 4; the same effect is achieved with the
utility-class flexbox toolkit (see Layout: Float, Flexbox & Grid for the
underlying flexbox model):
<form class="d-flex align-items-center gap-2">
<label class="visually-hidden" for="search-inline">Search</label>
<input type="text" class="form-control" id="search-inline" placeholder="Search...">
<button class="btn btn-primary" type="submit">Go</button>
</form>
.d-flex lays the form’s direct children out in a row, .align-items-center vertically centers a label
against a taller control, and .gap-* replaces manual margins between them.
Horizontal forms (grid-based)
A horizontal form — label to the left, control to the right, on the same row — is built directly on
Bootstrap’s .row/.col-* grid rather than a form-specific class:
<form>
<div class="row mb-3">
<label for="email-h" class="col-sm-2 col-form-label">Email</label>
<div class="col-sm-10">
<input type="email" class="form-control" id="email-h">
</div>
</div>
<div class="row mb-3">
<label for="password-h" class="col-sm-2 col-form-label">Password</label>
<div class="col-sm-10">
<input type="password" class="form-control" id="password-h">
</div>
</div>
<div class="row mb-3">
<div class="col-sm-10 offset-sm-2">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="remember-h">
<label class="form-check-label" for="remember-h">Remember me</label>
</div>
</div>
</div>
<button type="submit" class="btn btn-primary">Sign in</button>
</form>
.col-form-label vertically aligns the label’s text baseline with the control’s text, since a <label> and a
bordered .form-control do not naturally line up at the same height. .offset-sm-2 on the checkbox row lines
it up under the input column even though it has no label column of its own.
Multi-column layouts with the grid
Placing several fields side by side on one row (a city/state/zip triplet, for instance) is the same grid mechanism, just split into narrower columns instead of a label/input split:
<form class="row g-3">
<div class="col-md-6">
<label for="city" class="form-label">City</label>
<input type="text" class="form-control" id="city">
</div>
<div class="col-md-4">
<label for="state" class="form-label">State</label>
<select class="form-select" id="state">
<option selected>Choose...</option>
</select>
</div>
<div class="col-md-2">
<label for="zip" class="form-label">Zip</label>
<input type="text" class="form-control" id="zip">
</div>
</form>
.row.g-3 on the <form> itself applies grid gutters between the field columns; .col-md-6/.col-md-4/
.col-md-2 add up to the full 12-column row and only take effect at the md breakpoint and above, so the
fields stack to full width on narrow screens automatically.
Input groups
.input-group prepends or appends non-form content — an icon, a unit, a button — directly against a control’s
edge, sharing one visual border:
<div class="input-group mb-3">
<span class="input-group-text">@</span>
<input type="text" class="form-control" placeholder="Username" aria-label="Username">
</div>
<div class="input-group mb-3">
<input type="text" class="form-control" placeholder="Amount" aria-label="Amount">
<span class="input-group-text">.00</span>
</div>
<div class="input-group">
<input type="text" class="form-control" placeholder="Recipient's username" aria-label="Recipient's username">
<button class="btn btn-outline-secondary" type="button">Button</button>
</div>
An input group can prepend and append at once, and can hold more than one addon on either side; every addon
must be a sibling of the .form-control, not a wrapper around it, for the shared border to render correctly.
Floating labels
.form-floating collapses the label into placeholder-like text inside the control until the control gains
focus or a value, at which point the label animates up above it — a compact alternative to a separate
<label> row that still keeps a real, associated <label> for accessibility:
<div class="form-floating mb-3">
<input type="email" class="form-control" id="floating-email" placeholder="name@example.com">
<label for="floating-email">Email address</label>
</div>
<div class="form-floating">
<input type="password" class="form-control" id="floating-password" placeholder="Password">
<label for="floating-password">Password</label>
</div>
The placeholder attribute is still required even though the placeholder text is rarely seen — Bootstrap’s
floating-label CSS uses the presence of a placeholder as one of the signals for whether the field is "empty".
.form-select and .form-control textareas both support .form-floating as well.
Where to go next
Once controls are laid out, Forms: Validation States & Feedback covers marking a field valid or invalid and showing feedback text next to it. For the official reference, see the official Forms documentation.