Bootstrap Forms and Input Groups: Bootstrap 5 Guide
Bootstrap forms are regular HTML forms with a few classes that give every input the same border, padding, and focus ring, plus layout helpers and validation styles. This guide covers the basic form controls, labels and help text, form layouts (stacked, grid, horizontal, inline), floating labels, input groups, validation, and the less common controls: file, range, and color inputs. Checkboxes and selects have their own guides, linked below. Everything is for Bootstrap 5.3.
A basic form
<form>
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<input type="email" class="form-control" id="email" aria-describedby="email-help">
<div id="email-help" class="form-text">We'll only use it to send your receipt.</div>
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password" class="form-control" id="password">
</div>
<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input" id="remember">
<label class="form-check-label" for="remember">Remember me</label>
</div>
<button type="submit" class="btn btn-primary">Sign in</button>
</form>
| Class | Use on |
|---|---|
form-control |
Text, email, password, number, date inputs, and <textarea> |
form-select |
<select> dropdowns (see the select guide) |
form-check, form-check-input |
Checkboxes, radios, and switches (see the checkbox guide) |
form-label |
Labels above a field |
form-text |
Help text under a field |
mb-3 |
Space between fields (see the spacing guide) |
Every field needs a <label> whose for matches the input’s id. Connect help text with aria-describedby so screen readers read it with the field. Placeholders are hints, not labels: they disappear as soon as someone types.
Sizes, read-only, and disabled
<input class="form-control form-control-lg" type="text" aria-label="Large input">
<input class="form-control form-control-sm" type="text" aria-label="Small input">
<input class="form-control" type="text" value="ana@example.com" readonly aria-label="Email">
<input class="form-control-plaintext" type="text" value="ana@example.com" readonly aria-label="Email">
<fieldset disabled>
<legend class="fs-6">Billing (locked while a payment is processing)</legend>
<input class="form-control" type="text" aria-label="Card name">
</fieldset>
form-control-plaintext shows a read-only value as plain text with the same spacing as an input. A disabled <fieldset> disables every field inside it at once.
Form layouts with the grid
Put fields in grid columns. g-3 adds gutters between them, in both directions:
<form class="row g-3">
<div class="col-md-6">
<label for="first" class="form-label">First name</label>
<input type="text" class="form-control" id="first" autocomplete="given-name">
</div>
<div class="col-md-6">
<label for="last" class="form-label">Last name</label>
<input type="text" class="form-control" id="last" autocomplete="family-name">
</div>
<div class="col-12">
<label for="address" class="form-label">Address</label>
<input type="text" class="form-control" id="address" autocomplete="street-address">
</div>
<div class="col-md-5">
<label for="city" class="form-label">City</label>
<input type="text" class="form-control" id="city" autocomplete="address-level2">
</div>
<div class="col-md-3">
<label for="zip" class="form-label">ZIP</label>
<input type="text" class="form-control" id="zip" autocomplete="postal-code">
</div>
<div class="col-12">
<button type="submit" class="btn btn-primary">Continue</button>
</div>
</form>
Fields sit side by side from the md breakpoint and stack on phones. autocomplete values let browsers fill in saved details.
Horizontal forms
Labels on the left, fields on the right:
<form>
<div class="row mb-3">
<label for="h-email" class="col-sm-3 col-form-label">Email</label>
<div class="col-sm-9">
<input type="email" class="form-control" id="h-email">
</div>
</div>
<div class="row mb-3">
<label for="h-role" class="col-sm-3 col-form-label">Role</label>
<div class="col-sm-9">
<select class="form-select" id="h-role"><option>Editor</option><option>Viewer</option></select>
</div>
</div>
</form>
col-form-label lines the label up with the middle of the field.
Inline forms
For a search bar or a newsletter sign-up on one line:
<form class="row row-cols-lg-auto g-2 align-items-center">
<div class="col-12">
<label class="visually-hidden" for="newsletter-email">Email</label>
<input type="email" class="form-control" id="newsletter-email" placeholder="you@example.com">
</div>
<div class="col-12">
<button type="submit" class="btn btn-primary">Subscribe</button>
</div>
</form>
row-cols-lg-auto makes each column as wide as its content from the lg breakpoint, and the fields stack on smaller screens.
Floating labels
The label sits inside the field and moves up when you type:
<div class="form-floating mb-3">
<input type="email" class="form-control" id="f-email" placeholder="name@example.com">
<label for="f-email">Email address</label>
</div>
<div class="form-floating">
<textarea class="form-control" id="f-message" placeholder="Message" style="height: 120px"></textarea>
<label for="f-message">Message</label>
</div>
Two rules: the input comes before the label, and the input needs a placeholder (any text), because Bootstrap uses it to detect an empty field.
Input groups
An input group attaches text, icons, or buttons to the side of a field:
<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">
<span class="input-group-text">$</span>
<input type="number" class="form-control" aria-label="Amount in dollars">
<span class="input-group-text">.00</span>
</div>
<div class="input-group mb-3">
<input type="text" class="form-control" placeholder="Search docs" aria-label="Search docs">
<button class="btn btn-primary" type="button">Search</button>
</div>
<div class="input-group">
<input type="text" class="form-control" value="https://bootstrap.build/r/x7k2" readonly aria-label="Share link">
<button class="btn btn-outline-secondary" type="button">Copy</button>
</div>
- Text and icons go in
.input-group-text. An icon works too:<span class="input-group-text"><i class="bi bi-search"></i></span>. - Buttons go straight into the group, and dropdowns work there as well.
- Sizes:
input-group-smorinput-group-lgon the wrapper resizes every part. - Several inputs can share one group, such as first and last name.
- A checkbox goes in
.input-group-textwithform-check-input mt-0.
Validation
Bootstrap styles fields as valid or invalid using the browser’s own validation (required, type="email", minlength, and so on). Add novalidate to turn off the browser’s default bubbles, then show Bootstrap’s styles on submit:
<form class="needs-validation" novalidate>
<div class="mb-3">
<label for="v-email" class="form-label">Email</label>
<input type="email" class="form-control" id="v-email" required>
<div class="invalid-feedback">Enter a valid email address.</div>
</div>
<div class="mb-3">
<label for="v-user" class="form-label">Username</label>
<div class="input-group has-validation">
<span class="input-group-text">@</span>
<input type="text" class="form-control" id="v-user" required minlength="3">
<div class="invalid-feedback">At least 3 characters.</div>
</div>
</div>
<button class="btn btn-primary" type="submit">Create account</button>
</form>
document.querySelectorAll('.needs-validation').forEach(form => {
form.addEventListener('submit', event => {
if (!form.checkValidity()) {
event.preventDefault()
event.stopPropagation()
}
form.classList.add('was-validated')
})
})
was-validatedon the form shows red or green styles on every field, with the matchinginvalid-feedbackorvalid-feedbacktext.has-validationon an input group keeps its rounded corners right when a message appears.- Server-side errors: skip the script and add
is-invalid(oris-valid) to the field itself, with aninvalid-feedbackafter it. - Tooltip-style messages: use
invalid-tooltipinstead ofinvalid-feedback, withposition-relativeon the parent.
File, range, and color inputs
<label for="avatar" class="form-label">Profile photo</label>
<input class="form-control" type="file" id="avatar" accept="image/*">
<label for="volume" class="form-label">Volume</label>
<input type="range" class="form-range" id="volume" min="0" max="10" step="1">
<label for="brand" class="form-label">Brand color</label>
<input type="color" class="form-control form-control-color" id="brand" value="#2f55ff">
File inputs use form-control, range sliders use form-range, and color pickers use form-control plus form-control-color.
Style form controls
The focus ring and the checked color follow $primary, so changing the primary color updates them. For individual changes, in CSS:
.form-control, .form-select {
border-radius: .75rem;
border-color: #c9d4ff;
}
.form-control:focus, .form-select:focus {
border-color: #2f55ff;
box-shadow: 0 0 0 .25rem rgba(47, 85, 255, .25);
}
In Sass, $input-padding-y, $input-padding-x, $input-border-radius, and $input-focus-box-shadow set the defaults for every input, select, and input group at once. See Bootstrap Sass variables explained.
FAQ
How do I create a form in Bootstrap 5?
Use normal HTML form elements with Bootstrap classes: form-label on labels, form-control on inputs and textareas, form-select on selects, and form-check on checkboxes. Wrap each field in a div with mb-3 for spacing.
How do I put two form fields side by side in Bootstrap?
Use the grid: give the form class="row g-3" and wrap each field in a column such as col-md-6. The fields sit side by side from the medium breakpoint and stack on phones.
What is an input group in Bootstrap?
An input group attaches text, icons, buttons, or dropdowns to the side of a form field, such as an @ before a username or a Search button after a search box. Wrap the field and the extras in <div class="input-group"> and put text in input-group-text.
How do I show validation errors in a Bootstrap form?
Add novalidate to the form and an invalid-feedback element after each field, then add the was-validated class to the form on submit. For errors from the server, add is-invalid to the field directly.
How do I use floating labels in Bootstrap 5?
Wrap the input and its label in <div class="form-floating">, with the input first and the label second. The input needs a placeholder attribute, even though it isn’t shown, so Bootstrap can tell when the field is empty.
Try it in the builder
Make these changes visually with a live preview, then export clean Bootstrap Sass or CSS.
Open the Builder