Bootstrap Forms — Complete Guide to Inputs, Validation & Layout
Bootstrap provides a complete form system with consistent styling for all input types, layout options, and built-in client-side validation that works across every browser and device.
What You’ll Learn
- Style text inputs, textareas, selects, and file inputs with
form-control - Use checkboxes, radios, and toggle switches with
form-check - Build input groups with prepend/append text and buttons
- Implement floating labels and horizontal/inline form layouts
- Add client-side validation with visual feedback
Why Forms Matter
Forms are how users interact with your application — signing up, logging in, submitting data, configuring settings. A poorly designed form frustrates users and causes abandonment.
At DodaTech, the Durga Antivirus Pro registration page, Doda Browser settings panel, and DodaZIP file upload dialogs all use Bootstrap forms. The built-in validation catches errors before they reach the server, saving bandwidth and improving user experience.
Your Learning Path
flowchart LR
A[Bootstrap Content] --> B[Bootstrap Components 1-3]
B --> C[Bootstrap Forms]
C --> D[Bootstrap Utilities]
D --> E[Bootstrap Advanced]
C:::current
classDef current fill:#0d6efd,color:#fff,stroke:#0a58ca,stroke-width:2px
Form Control — Text Inputs
The .form-control class transforms a basic <input> into a consistently styled Bootstrap input:
<!-- The mb-3 adds margin-bottom for spacing between form groups.
Always wrap inputs in a div with a label for accessibility. -->
<div class="mb-3">
<label for="name" class="form-label">Name</label>
<!-- form-control gives the input consistent padding, border,
font-size, and focus styling across all browsers. -->
<input type="text" class="form-control" id="name" placeholder="Enter your name">
</div>
<!-- Size variants: form-control-lg and form-control-sm -->
<input class="form-control form-control-lg" type="text" placeholder="Large input">
<input class="form-control" type="text" placeholder="Default">
<input class="form-control form-control-sm" type="text" placeholder="Small input">
<!-- Disabled: grayed out, not focusable, not submittable -->
<input class="form-control" type="text" value="Cannot edit" disabled>
<!-- Readonly: looks normal but value can't be changed. Still submittable. -->
<input class="form-control" type="text" value="Cannot change" readonly>
<!-- form-control-plaintext: readonly text that looks like plain text
(no border/background) but stays aligned in the form grid -->
<input class="form-control-plaintext" type="text" value="email@example.com" readonly>
<!-- File input — for file uploads -->
<div class="mb-3">
<label for="formFile" class="form-label">Upload file</label>
<input class="form-control" type="file" id="formFile">
</div>
<!-- Color input — native color picker styled with Bootstrap -->
<div class="mb-3">
<label for="colorPicker" class="form-label">Choose color</label>
<input type="color" class="form-control form-control-color" id="colorPicker" value="#1a73e8">
</div>
<!-- Textarea — same form-control class works on <textarea> -->
<div class="mb-3">
<label for="bio" class="form-label">Bio</label>
<textarea class="form-control" id="bio" rows="3"></textarea>
</div>Why form-label on labels? Labels get margin-bottom: 0.5rem and proper font sizing. Without it, labels appear too large or misaligned.
Select Menus
Select elements use form-select (not form-control):
<select class="form-select">
<option selected>Open this select menu</option>
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
<!-- Size variants -->
<select class="form-select form-select-lg">
<select class="form-select form-select-sm">
<!-- Multiple: allows selecting several options with Ctrl+click -->
<select class="form-select" multiple>
<option value="html">HTML</option>
<option value="css">CSS</option>
<option value="js">JavaScript</option>
</select>
<select class="form-select" disabled>Checkboxes & Radios
All checkboxes and radios use the form-check family of classes:
<!-- Checkbox: form-check wraps the input and label together -->
<div class="form-check">
<!-- form-check-input styles the checkbox itself -->
<input class="form-check-input" type="checkbox" id="agree">
<!-- form-check-label provides proper spacing and cursor -->
<label class="form-check-label" for="agree">I agree to terms</label>
</div>
<!-- Multiple checkboxes: each in its own form-check div -->
<div class="form-check">
<input class="form-check-input" type="checkbox" value="html" id="htmlCheck">
<label class="form-check-label" for="htmlCheck">HTML</label>
</div>
<!-- Inline checkboxes: form-check-inline places them side by side -->
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="inline1">
<label class="form-check-label" for="inline1">Option 1</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="inline2">
<label class="form-check-label" for="inline2">Option 2</label>
</div>
<!-- Radio buttons: same name attribute = one selection only -->
<div class="form-check">
<input class="form-check-input" type="radio" name="gender" id="male">
<label class="form-check-label" for="male">Male</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="gender" id="female">
<label class="form-check-label" for="female">Female</label>
</div>
<!-- Toggle switch: form-switch replaces the checkbox square
with a sliding toggle (iOS-style) -->
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="notifySwitch">
<label class="form-check-label" for="notifySwitch">Enable notifications</label>
</div>Range Slider
<!-- Custom-styled range input -->
<label for="priceRange" class="form-label">Price: <span id="priceVal">50</span></label>
<input type="range" class="form-range" id="priceRange" min="0" max="100" step="1" value="50">
<!-- JavaScript to show the current value -->
<script>
document.getElementById("priceRange").addEventListener("input", function() {
document.getElementById("priceVal").textContent = this.value;
});
</script>Input Groups — Attaching Text or Buttons to Inputs
Input groups let you attach symbols, text, or buttons to the left or right of an input:
<!-- Prepend: text before the input (@ for username) -->
<div class="input-group mb-3">
<span class="input-group-text">@</span>
<input type="text" class="form-control" placeholder="Username">
</div>
<!-- Append: text after the input -->
<div class="input-group mb-3">
<input type="text" class="form-control" placeholder="Amount">
<span class="input-group-text">.00</span>
</div>
<!-- Both sides: currency symbol + decimal -->
<div class="input-group mb-3">
<span class="input-group-text">$</span>
<input type="text" class="form-control">
<span class="input-group-text">.00</span>
</div>
<!-- Button in input group (like a search bar) -->
<div class="input-group mb-3">
<input type="text" class="form-control" placeholder="Search">
<button class="btn btn-primary" type="button">Search</button>
</div>
<!-- Multiple inputs in one group -->
<div class="input-group">
<span class="input-group-text">Full name</span>
<input type="text" class="form-control" placeholder="First">
<input type="text" class="form-control" placeholder="Last">
</div>Floating Labels — Placeholder That Moves
Floating labels create a modern UI where the label sits inside the input and “floats” above it when focused:
<!-- The placeholder attribute is REQUIRED — Bootstrap uses it to
detect whether the input has content. Without it, the label
never floats. -->
<div class="form-floating mb-3">
<input type="email" class="form-control" id="floatingEmail" placeholder="name@example.com">
<label for="floatingEmail">Email address</label>
</div>
<!-- Textarea with floating label -->
<div class="form-floating mb-3">
<textarea class="form-control" id="floatingTextarea" placeholder="Leave a comment"></textarea>
<label for="floatingTextarea">Comments</label>
</div>
<!-- Select with floating label -->
<div class="form-floating mb-3">
<select class="form-select" id="floatingSelect">
<option selected>Open this select</option>
<option value="1">Option 1</option>
</select>
<label for="floatingSelect">Select option</label>
</div>Form Layouts
Stacked (Default) — Labels Above Inputs
<form>
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<input type="email" class="form-control" id="email">
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password" class="form-control" id="password">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>Horizontal — Labels Beside Inputs (Using the Grid)
<form>
<!-- The grid row creates a horizontal layout.
Label takes 2 columns, input takes 10. -->
<div class="row mb-3">
<label for="name" class="col-sm-2 col-form-label">Name</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="name">
</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">
<label class="form-check-label" for="remember">Remember me</label>
</div>
</div>
</div>
<button type="submit" class="btn btn-primary">Sign in</button>
</form>Inline — All Elements on One Row
<form class="row g-3 align-items-center">
<div class="col-auto">
<label for="inlineEmail" class="visually-hidden">Email</label>
<input type="email" class="form-control" id="inlineEmail" placeholder="Email">
</div>
<div class="col-auto">
<label for="inlinePassword" class="visually-hidden">Password</label>
<input type="password" class="form-control" id="inlinePassword" placeholder="Password">
</div>
<div class="col-auto">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>Form Validation
Bootstrap gives you two validation styles: feedback (below input) and tooltip (floating above).
Browser-Native Validation (Simpler)
Add the was-validated class to the <form> after submission to trigger Bootstrap’s validation styles:
<form class="was-validated" novalidate>
<div class="mb-3">
<label for="name3" class="form-label">Name</label>
<input type="text" class="form-control" id="name3" required>
<!-- valid-feedback shows green when valid -->
<div class="valid-feedback">Looks good!</div>
<!-- invalid-feedback shows red when invalid -->
<div class="invalid-feedback">Please enter your name.</div>
</div>
<div class="mb-3">
<label for="email4" class="form-label">Email</label>
<input type="email" class="form-control" id="email4" required>
<div class="invalid-feedback">Please enter a valid email.</div>
</div>
<div class="form-check mb-3">
<input type="checkbox" class="form-check-input" id="agreeCheck" required>
<label class="form-check-label" for="agreeCheck">Agree to terms</label>
<div class="invalid-feedback">You must agree.</div>
</div>
<button class="btn btn-primary" type="submit">Submit</button>
</form>JavaScript Validation (Better UX)
<form class="row g-3" id="validationForm" novalidate>
<div class="col-md-4">
<label class="form-label">First name</label>
<input type="text" class="form-control" required>
<div class="valid-feedback">Good!</div>
<div class="invalid-feedback">Required.</div>
</div>
<div class="col-12">
<button class="btn btn-primary" type="submit">Submit</button>
</div>
</form>
<script>
(function() {
var form = document.getElementById("validationForm");
form.addEventListener("submit", function(e) {
// preventDefault stops the page from reloading
e.preventDefault();
e.stopPropagation();
// checkValidity() is a native HTML5 method that returns
// true if all required inputs have values
if (form.checkValidity()) {
alert("Form submitted successfully!");
}
// Adding was-validated triggers Bootstrap's green/red styles
form.classList.add("was-validated");
}, false);
})();
</script>Why novalidate? The novalidate attribute on the form disables the browser’s default validation popups (which look different across browsers) so Bootstrap’s styled validation takes over.
Common Mistakes
1. Using form-control on select elements
Use form-select (not form-control) for <select>. They look different and form-control doesn’t style the dropdown arrow correctly.
2. Missing for attribute on labels
The for attribute must match the input’s id. This is critical for accessibility — screen readers need it, and it makes the label clickable to focus the input.
3. Forgetting novalidate with custom validation
Without novalidate, the browser shows its native validation bubbles (which can’t be styled) alongside Bootstrap’s styled messages.
4. Not using was-validated class
Validation styles only appear after was-validated is added to the form. Without it, valid-feedback and invalid-feedback remain hidden.
5. Omitting placeholder on floating labels
Floating labels need a placeholder attribute to work. Bootstrap uses CSS :not(:placeholder-shown) to detect whether the input has content.
Practice Questions
Why do select elements use
form-selectinstead ofform-control? Answer: Selects have different styling requirements (dropdown arrow, different height behavior). Usingform-selectgives the correct appearance for dropdown menus.What does
novalidatedo on a form? Answer: It disables the browser’s built-in validation tooltips/notifications so that Bootstrap’s styled validation messages can be used instead.How does
form-floatingknow when to float the label? Answer: It uses the CSS pseudo-class:not(:placeholder-shown)on the input. When the placeholder is not visible (because the user typed something), the label floats up.What’s the difference between
disabledandreadonlyon an input? Answer: Disabled inputs are grayed out, can’t be focused, and their values are NOT submitted with the form. Readonly inputs look normal, can be focused, and their values ARE submitted.How do you show validation errors as tooltips instead of text blocks? Answer: Replace
invalid-feedbackwithinvalid-tooltip. The error message appears as a floating tooltip near the input.
Challenge: Build a registration form with 6 fields (first name, last name, email, password, country select, agree checkbox), proper validation on all required fields, and a toggle switch for newsletter opt-in. Use input groups for the email (with @ prefix).
FAQ
Try It Yourself
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Register</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container py-5">
<div class="row justify-content-center">
<div class="col-lg-8">
<h1 class="mb-4">Create Account</h1>
<form id="registerForm" novalidate>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">First Name</label>
<input type="text" class="form-control" required>
<div class="invalid-feedback">First name is required.</div>
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Last Name</label>
<input type="text" class="form-control" required>
<div class="invalid-feedback">Last name is required.</div>
</div>
</div>
<div class="mb-3">
<label class="form-label">Email</label>
<div class="input-group">
<span class="input-group-text">@</span>
<input type="email" class="form-control" placeholder="name@example.com" required>
<div class="invalid-feedback">Valid email is required.</div>
</div>
</div>
<div class="mb-3">
<label class="form-label">Password</label>
<input type="password" class="form-control" minlength="8" required>
<div class="form-text">Min 8 characters.</div>
<div class="invalid-feedback">Password must be 8+ characters.</div>
</div>
<div class="mb-3">
<label class="form-label">Country</label>
<select class="form-select" required>
<option value="">Choose...</option>
<option>US</option>
<option>UK</option>
<option>Canada</option>
</select>
<div class="invalid-feedback">Please select a country.</div>
</div>
<div class="mb-3">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="newsletter">
<label class="form-check-label" for="newsletter">Subscribe to newsletter</label>
</div>
</div>
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" id="terms" required>
<label class="form-check-label" for="terms">I agree to the <a href="#">terms of service</a></label>
<div class="invalid-feedback">You must agree to continue.</div>
</div>
<button type="submit" class="btn btn-primary btn-lg w-100">Create Account</button>
</form>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
document.getElementById("registerForm").addEventListener("submit", function(e) {
e.preventDefault();
this.classList.add("was-validated");
if (this.checkValidity()) {
alert("Registration submitted! (Demo)");
}
});
</script>
</body>
</html>This sandbox demonstrates: form inputs, selects, checkboxes, input groups, form validation, grid layout, toggle switches, and JavaScript validation.
What’s Next
| Topic | Description |
|---|---|
| Bootstrap Utilities | Flexbox, spacing, display, and sizing helpers |
| Bootstrap Advanced | Dark mode, Sass customization, JavaScript API |
| JavaScript Forms | Advanced JavaScript form handling |
| HTML Forms | Native HTML form elements and attributes |
What’s Next
Congratulations on completing this Bootstrap Forms tutorial! Here’s where to go from here:
- Practice daily — Consistency is more important than long study sessions
- Build a project — Apply what you learned by building something real
- Explore related topics — Check out other tutorials in the same category
- Join the community — Discuss with other learners and share your progress
Remember: every expert was once a beginner. Keep coding!
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro