Skip to content
Bootstrap Forms — Complete Guide to Inputs, Validation & Layout

Bootstrap Forms — Complete Guide to Inputs, Validation & Layout

DodaTech Updated Jun 6, 2026 10 min read

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
  
Prerequisites: This tutorial assumes you know HTML basics (form, input, label, select elements) and have completed the Bootstrap Basics for setup. JavaScript is needed for the validation section.

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

  1. Why do select elements use form-select instead of form-control? Answer: Selects have different styling requirements (dropdown arrow, different height behavior). Using form-select gives the correct appearance for dropdown menus.

  2. What does novalidate do 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.

  3. How does form-floating know 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.

  4. What’s the difference between disabled and readonly on 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.

  5. How do you show validation errors as tooltips instead of text blocks? Answer: Replace invalid-feedback with invalid-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

What is the difference between form-control-plaintext and form-control?
form-control-plaintext removes borders and background for read-only text that appears like plain text but still participates in layout. Use for readonly data display in forms.
How do I create an inline form in Bootstrap 5?
Use the grid system with .row and .col-auto, or the flex utility d-flex. Each input needs a label (use visually-hidden if needed).
What does the form-floating class do?
It creates a floating label that animates above the input when focused or filled. The label starts inside the input, then “floats” up.
How do I validate a form on submit without page reload?
Prevent default submission with e.preventDefault(), check form.checkValidity(), and conditionally proceed. Bootstrap’s was-validated class triggers the visual states.
Can I use tooltip-style validation messages?
Yes, replace invalid-feedback with invalid-tooltip. The error message appears as a tooltip above/below the input.
What is the input-group for?
Input groups attach text, buttons, or icons to the side of an input — useful for currency symbols, search buttons, or username prefixes.

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

TopicDescription
Bootstrap UtilitiesFlexbox, spacing, display, and sizing helpers
Bootstrap AdvancedDark mode, Sass customization, JavaScript API
JavaScript FormsAdvanced JavaScript form handling
HTML FormsNative 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