What is a Bootstrap input?
Add .form-control to a native input or textarea and Bootstrap restyles it. Every field gets the same borders, padding, focus ring and state styling, which keeps rendering consistent across browsers and devices (Bootstrap documentation). Nothing replaces the underlying element.
The field is still a plain HTML element, so the type attribute decides which built-in controls and checks the browser applies. The Bootstrap docs advise email for addresses and number for numeric values.
A single field usually starts with a <label class="form-label"> whose for value matches the input id. The input follows with .form-control, the same id and a suitable type. Both sit in a div with .mb-3 for spacing, because Bootstrap 5 uses margin utilities instead of a form-group class. If the field needs a hint, a .form-text block goes underneath and connects to the input through aria-describedby.
That pattern repeats for every field. How the rows line up is a layout question, and the full Bootstrap form guide covers it.
Examples here target Bootstrap 5.3. The official docs list v5.3.8 as the current release as of October 2026, and Bootstrap 4 markup differs in several places.
Which input types does Bootstrap style?
Text-like types take .form-control. Color, range and select get their own classes.
| Input type | Bootstrap class | Note |
|---|
| text, email, password, number, date, url, textarea | .form-control | Appearance only, parsing stays with the browser |
| file | .form-control | Add multiple for several files, sm and lg sizes work |
| color | .form-control and .form-control-color | Needs type=”color”, width set by $form-color-width |
| range | .form-range | Replaces the browser slider look |
| select | .form-select | Separate class, not .form-control |
Number, date and email fields still validate the way the browser decides. Bootstrap changes how they look, not what they accept.
Datalist is a bit awkward. The text field takes .form-control, but the browser draws the suggestion list, and the docs call that styling inconsistent across browsers and operating systems. File inputs are simpler, since one class covers single and multiple uploads.
Checkboxes and radios sit outside this group. They use .form-check-input inside a .form-check wrapper, which the Bootstrap checkbox styles cover separately.
How to change Bootstrap input size and width
Add .form-control-sm to shrink a field or .form-control-lg to enlarge it. Width comes from the parent column, because .form-control fills 100% of its container by default.
Sizing with classes
Both size classes change padding and font size, so the field height follows. The padding values below come from the shared $input-btn-* Sass variables in v5.3.8, which buttons use too.
| Class | Vertical padding | Horizontal padding |
|---|
| .form-control (default) | 0.375rem | 0.75rem |
| .form-control-sm | 0.25rem | 0.5rem |
| .form-control-lg | 0.5rem | 1rem |
Horizontal forms need one more class. Labels take .col-form-label-sm or .col-form-label-lg so they track the input size, according to the Bootstrap layout docs.
Input groups size from the wrapper instead, with .input-group-sm or .input-group-lg.
Setting width with the grid
Fields stack by default, since Bootstrap applies display: block and width: 100% to almost every form control. For side-by-side fields, wrap them in .row g-3 and give each one a .col-md-* column. .col-auto sizes a column to its content, and if you only want a fixed share, a width utility such as .w-50 on the input itself does the job.
Column classes come from Bootstrap’s grid system, and the breakpoint suffix (md, lg) decides where columns stack.
A field that is narrower than its parent usually lost .form-control or inherited a custom width rule. Check those two first.
Disabled, readonly and plaintext inputs
Disabled blocks all interaction, readonly keeps the field focusable, and .form-control-plaintext strips the field styling from a readonly input.
A disabled field looks lighter than a normal one, gets skipped by keyboard focus and is left out of the submitted data. A readonly field stays in the tab order, its text can be selected and copied, and its value is submitted.
Readonly fits values people need to copy, like an order ID. Disabled text cannot be selected, according to the Bootstrap docs.
For plaintext, swap .form-control for .form-control-plaintext on the readonly input. The border and background disappear, margin and padding stay, and in 5.3 the text color follows the body color variable.
Putting disabled on a <fieldset> disables every native control inside it. Link-styled buttons are the exception. An <a class="btn"> only receives pointer-events: none, so keyboard users can still reach it.
Add tabindex="-1" and the aria-disabled attribute to those links, so the ARIA state reaches assistive technologies.
The focus ring is the one state left to the framework. Its width, blur and color come from the $input-btn-focus-* variables, which point to the $focus-ring-* variables in v5.3.8.
How does Bootstrap input validation work?
Bootstrap styles the browser’s :valid and :invalid pseudo-classes once a parent .was-validated class is present. Without that class, every empty required field would show as invalid the moment the page loads.
The styles reach input and textarea elements with .form-control, selects with .form-select, and .form-check groups.
Client-side validation
Add novalidate to the form to hide the browser’s default error bubbles. The constraint validation API stays available, so a short JavaScript handler can still call checkValidity().
- Add
novalidate and the needs-validation class to the form. - Mark each field
required or give it a pattern the browser can check. - Place an
.invalid-feedback block after each field, plus .valid-feedback for success messages. - Attach a submit listener that calls
checkValidity() and, on failure, runs preventDefault() and stopPropagation(). - Add
.was-validated to the form inside the same listener.
After an Ajax submission, remove .was-validated to reset the form’s appearance. Custom messages come from setCustomValidity.
There is an accessibility catch here. The Bootstrap 5.3 documentation states that client-side custom validation styles and tooltips are not exposed to assistive technologies.
For anyone building accessible forms, the docs point to the server-side route or the browser’s default validation messages instead. The .invalid-tooltip variant falls under the same warning.
Server-side states
.is-valid and .is-invalid work without a .was-validated parent. An .invalid-feedback block next to the field appears once the state class is on the input. Link each message to its field with aria-describedby, which accepts several ids when the field already points to help text.
The docs recommend client-side validation in general, yet the accessibility note steers cautious teams toward server-rendered states. For production forms, browser defaults or server classes are the safer pair, and that is where I would land too.
Validation inside input groups
Input groups need an extra .has-validation class on the wrapper to fix border radius issues, according to the Bootstrap docs.
Place the feedback block inside the group, as a sibling of the input. Validation styles apply to one .form-control per input group, so a group with two text fields needs separate handling.
Floating labels or input groups: which wrapper fits?
Floating labels suit short single-line fields where space is tight, and an input group fits when the field needs something attached, such as a prefix or a button. For long or dense forms, a plain input with a visible label is still the better call.
Floating labels
Wrapping an input and its label in .form-floating produces floating labels. Two markup rules decide whether the effect works. Every input needs a non-empty placeholder, because the CSS-only version detects an empty field with :placeholder-shown. And the input comes first with the label second, so a sibling selector can reach the label.
The placeholder text never shows. Only the label is visible, according to the Bootstrap 5.3 docs.
Coverage is narrower than a plain input. Floating labels work on .form-control fields (inputs and textareas) and on .form-select, where the label always stays in its floated position.
Selects with size or multiple are not supported, and a custom textarea height needs an explicit height instead of the rows attribute.
On the plus side, the label stays visible after the user types, and short forms save vertical space. It also works with validation styles, disabled inputs and readonly plaintext.
The costs are real though. The field is taller, since $form-floating-height is 3.5rem plus the border. A placeholder is required even though nobody sees it. The floated label scales to 85% and drops to 0.65 opacity by default. And inside an input group, validation feedback has to be shown with JavaScript, where it sits outside .form-floating but inside the group.
Version 5.3.0 gave floating labels a background color to fix textarea support, per the Bootstrap migration notes.
Input groups
An input group attaches text, buttons or button groups to the edge of an input, and the result renders as one control.
Add-ons sit inside .input-group as .input-group-text elements or buttons. Icons from the Bootstrap Icons set fit the text add-on well for search and email fields.
A Bootstrap button works as the action add-on, for example a search or apply button next to the field.
Labels and help text go outside the group. Groups wrap by default (flex-wrap: wrap), and .flex-nowrap stops it. Select and file add-ons need .form-select and .form-control, because the browser default versions are not supported.
Border radius only reaches the first and last children. A hidden element such as one with .d-none in either position breaks the corners, and the docs say a fix will most likely wait for v6.
That is a different problem from the validation radius issue that .has-validation solves.
The upside is that prefix, suffix and action stay visually attached to the field, and one group takes checkboxes, radios, dropdowns and custom selects. On the downside, the label has to sit outside the group, hidden children break the corners, and validation styles only work with a single input per group.
Choosing between them
If the field needs something attached, use an input group. If the form is short and a label is the only extra, use floating labels.
A login or signup form with a handful of short fields suits floating labels. A search box, a currency amount or a username with an @ prefix wants an input group. Long label text, textareas with instructions and dense admin screens do better with a plain label above the field.
The two also combine. The docs place .form-floating inside .input-group, so a floating field can carry an add-on.
Which accessibility attributes does a Bootstrap input need?
A Bootstrap input needs an accessible name, a programmatic link to its help and error text, and an error message written in text. Bootstrap supplies the classes, and the markup that satisfies WCAG stays with the author.
The Bootstrap 5.3 docs say a visible label is generally the best approach, and they list four other ways to name an input. The ranking below is mine, since the docs do not order them.
A visible label comes first, meaning a <label> whose for value matches the input id. Next is the same label with .visually-hidden, for designs that drop the visible text. After that comes aria-labelledby, which points the input at an element that already holds the text, and then aria-label, which puts the name directly on the input, as Bootstrap’s own input group examples do. The title attribute works as a name but is the weakest explicit option.
Without any of these, assistive technologies can fall back to the placeholder, which disappears once the field has content. Hidden text also helps only screen reader users, so the docs still favor a visible label.
WCAG 2.2 splits the work across criteria. SC 3.3.2 (Level A) asks for labels or instructions on inputs, while SC 1.3.1 covers whether the label is programmatically tied to the control (W3C Understanding documents).
SC 3.3.1 (Level A) requires a detected input error to be identified and described in text. The color and icon from .is-invalid can signal the error, and the W3C text allows that, but .invalid-feedback carries the description.
Feedback text takes the danger color through $form-feedback-invalid-color. The color contrast of that red against your page background is yours to test after any theme change.
The classes cover the field. A web accessibility checklist covers the page around it.
How to customize Bootstrap input styles with Sass and CSS variables
Override $input-* Sass variables before Bootstrap’s variables load when you compile it yourself. If you ship prebuilt CSS, override --bs-* CSS variables or use data-bs-theme instead.
Overriding Sass variables
Every Bootstrap Sass variable carries the !default flag, so a value you assign first wins. Overrides must come after the functions import and before the variables import. The full order is functions, your overrides, then variables, variables-dark, maps, mixins and root.
The add-on background, $input-group-addon-bg, defaults to var(--bs-tertiary-bg) in v5.3.8, so it already follows the color mode.
Validation colors live in the $form-validation-states map, which holds color, icon, tooltip colors, focus shadow and border color for each state. Browsers only support valid and invalid, but the docs note that custom styles can add more.
Sass overrides only work when you compile. A CDN build is already compiled, so changing a variable there does nothing.
Recent Dart Sass versions print deprecation warnings when compiling Bootstrap’s source. The docs say these do not block compilation and can be ignored while a long-term fix is in progress.
Theming at runtime
Color modes arrived in Bootstrap 5.3.0, and .form-control was rebuilt on CSS variables to follow them.
data-bs-theme="dark" on the html element themes every input, and on a single container it themes only that block. Validation colors switch automatically through --bs-form-valid-color and --bs-form-invalid-color, using green-300 and red-300 based values in dark mode. The select arrow has its own dark SVG variable, $form-select-indicator-dark. Any data-bs-theme name works, such as blue, once you override the CSS variables for it.
Setting $enable-dark-mode to false removes the built-in dark mode. Switching $color-mode-type to media-query makes it automatic but gives up per-component switching, according to the Bootstrap docs.
The Bootstrap team states that CSS variable coverage stays incomplete until v6 (v5.2.0 migration notes), so some input properties still need Sass. For one-off changes on a CDN build, a plain stylesheet works best, as this guide to overriding Bootstrap CSS shows.
If you set custom borders and backgrounds, they need a 3:1 non-text contrast ratio against their surroundings, per the WCAG figure cited in the Bootstrap Sass docs.
What changed for inputs from Bootstrap 4 to Bootstrap 5?
Bootstrap 5 removed the form-specific layout classes, merged native and custom controls into one class set, and dropped the wrapper divs around input group add-ons.
| Bootstrap 4 | Bootstrap 5 | Effect on the input |
|---|
| .form-group | Margin utilities such as .mb-3 | Spacing moves to utilities |
| .form-row, .form-inline | .row with .g-* gutters, .col-auto | Layout moves to the grid |
| .input-group-prepend, .input-group-append | Direct children of .input-group | Wrapper divs deleted |
| .custom-select, .custom-range | .form-select, .form-range | One class per control |
| .custom-file, .form-control-file | .form-control | File input styled like a text input |
The online guides disagree on the last row. Some third-party migration material, including the MDB guide, maps .custom-file to .form-file. That class only existed in the early v5 alphas: v5.0.0-alpha1 added it and v5.0.0-alpha3 dropped it for .form-control.
The official Bootstrap 5 migration notes say custom styles on top of .form-control replaced both file classes, and the 5.3 form docs use only .form-control for file inputs. Follow the official notes.
Version 5.0.0 also made form labels require .form-label. .form-text no longer sets display, so inline or block help text depends on the element you choose. Controls use min-height instead of a fixed height where possible, and floating labels became a fully supported component.
Changes inside Bootstrap 5 matter too. Version 5.2.0 refined the border radius on form controls, a visible shift when upgrading from earlier 5.x releases. Version 5.3.0 moved .form-control onto CSS variables for color modes and fixed date and time input display in WebKit browsers.
Bootstrap 5 also dropped Internet Explorer 10 and 11, legacy Edge below 16, Firefox below 60 and Safari below 12 (migration notes). Teams that still serve those browsers face a cross-browser compatibility decision before they upgrade.
Bootstrap 4.6.2, published July 19, 2022, is the final release on the 4.x line, and Bootstrap 4 reached end of life on January 1, 2023 (Bootstrap blog and end-of-life notice). Bootstrap 5 is the only stable major version still maintained. Bootstrap 6 reached its first alpha on October 8, 2026 and is not production-ready (Bootstrap blog).
For existing forms, start with wrappers and labels. Input groups can wait a step, and the file and select classes go last.
Bootstrap Input FAQ
Does a Bootstrap input need jQuery?
No. Bootstrap 5 dropped jQuery, and the .form-control styling is plain CSS.
JavaScript appears only in the validation starter script and in dropdown buttons inside input groups, which need Bootstrap’s JavaScript plus Popper (the bundle file includes both).
Should I load Bootstrap from a CDN or install it with npm?
Use a CDN for prototypes and npm for customized builds. The official docs show jsDelivr links pinned to a version such as bootstrap@5.3.8.
An npm install places Bootstrap’s Sass source in node_modules, which you need before any $input-* override can take effect.
How do I use a Bootstrap input in React?
Use Form.Control from React-Bootstrap, which renders the same .form-control class. React-Bootstrap 2.0.0 and later targets Bootstrap 5.
Wrap fields in Form.Group to pass a controlId, since Bootstrap 5 removed the .form-group class itself.
Can Bootstrap validation replace server-side checks?
No. Bootstrap only styles the result, and client-side checks run in the browser, where users can change or bypass them.
Keep server-side validation in place, and use .is-invalid and .is-valid to display what the server returns.
When to Move Past Bootstrap Input Styling
A Bootstrap input remains the right choice until a form needs behavior that .form-control, .input-group and .form-floating cannot express. Beyond that point, custom CSS on the native element is the cleaner path.
Escalate slowly. Standard fields stay on .form-control, validation stays server-rendered or browser-default for as long as it can, and custom CSS is reserved for components that hit a documented limit.
That sequence keeps color mode support and consistent browser rendering for every field that needs no exception. The trade-off of the last step is losing both for the fields restyled by hand.
Two documented limits point the same way. The docs expect the input group radius fix in v6 and tie full CSS variable coverage to v6, so hand-written CSS for those components is the plan until a stable v6 ships (Bootstrap 6 reached its first alpha on October 8, 2026; position verified October 29, 2026, against v5.3.8).
A working Bootstrap contact form is a practical place to test these choices.