Input Groups & Floating Labels
Some fields make more sense with something attached: a ₹ sign before a price, "kg" after a weight, "@ethicsmail.in" after a username, or a "Search" button joined to the search box. Bootstrap's input groups join these pieces into one tidy control. Floating labels are a related trick: the label sits inside the field and slides up when the user types, which saves space on mobile forms.
In this lesson you will build both, and learn the HTML order and attributes each one depends on.
Input groups
Wrap the field and its add-ons in a div with input-group. Text add-ons go in a span with input-group-text. Buttons go in directly with their normal btn classes.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Input groups</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container py-3" style="max-width: 560px">
<label for="price" class="form-label">Selling price</label>
<div class="input-group mb-3">
<span class="input-group-text">₹</span>
<input type="number" class="form-control" id="price" min="0" aria-describedby="price-unit">
<span class="input-group-text" id="price-unit">per kg</span>
</div>
<label for="username" class="form-label">Choose an email address</label>
<div class="input-group mb-3">
<input type="text" class="form-control" id="username" aria-describedby="domain">
<span class="input-group-text" id="domain">@rohtakmail.in</span>
</div>
<label for="coupon" class="form-label">Coupon code</label>
<div class="input-group mb-3">
<input type="text" class="form-control" id="coupon" placeholder="DIWALI10">
<button class="btn btn-outline-success" type="button" id="applyBtn">Apply</button>
</div>
<p id="couponMsg" class="small"></p>
<label for="mobile" class="form-label">Mobile number</label>
<div class="input-group mb-3">
<select class="form-select flex-grow-0 w-auto" aria-label="Country code">
<option>+91</option>
<option>+971</option>
<option>+65</option>
</select>
<input type="tel" class="form-control" id="mobile" inputmode="numeric">
</div>
<p class="mb-1">Height (two inputs)</p>
<div class="input-group input-group-sm">
<input type="number" class="form-control" aria-label="Feet" placeholder="5">
<span class="input-group-text">ft</span>
<input type="number" class="form-control" aria-label="Inches" placeholder="8">
<span class="input-group-text">in</span>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"></script>
<script>
document.getElementById("applyBtn").addEventListener("click", () => {
const code = document.getElementById("coupon").value.trim().toUpperCase();
const msg = document.getElementById("couponMsg");
msg.textContent = code === "DIWALI10" ? "10% off applied." : "That coupon is not valid.";
msg.className = code === "DIWALI10" ? "small text-success" : "small text-danger";
});
</script>
</body>
</html>How the pieces fit:
- Add-ons can go before, after, or on both sides of the field. The order in the HTML is the order on screen. Bootstrap rounds only the outer corners of the group.
- Screen readers do not read
input-group-textas part of the label. If the add-on carries meaning (like "per kg" or the email domain), connect it witharia-describedby. - Each field still needs a label. When the group has two fields, give each an
aria-label, as in the height example. input-group-smandinput-group-lgsize the whole group at once.- A
form-selectcan sit inside a group too.flex-grow-0 w-autostops it from taking half the width.
Buttons, dropdowns and checkboxes in groups
You can put any number of buttons in a group, and a dropdown button can open a menu of choices. A checkbox or radio goes inside an input-group-text, with mt-0 to remove its normal top margin:
<div class="input-group mb-3">
<button class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">Search in</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#">Books</a></li>
<li><a class="dropdown-item" href="#">Stationery</a></li>
</ul>
<input type="search" class="form-control" aria-label="Search the shop">
<button class="btn btn-primary" type="submit">Search</button>
</div>
<div class="input-group">
<div class="input-group-text">
<input class="form-check-input mt-0" type="checkbox" aria-label="Use as billing address">
</div>
<input type="text" class="form-control" aria-label="Address" value="12, MG Road, Kochi">
</div>By default the parts of a group may wrap onto two lines on a very narrow screen. Add flex-nowrap to the group to keep them on one line.
Floating labels
A floating label starts inside the field, looking like a placeholder. When the field gets focus or has a value, the label shrinks and moves to the top edge, so it stays visible. You get a compact form that never loses its labels.
The structure is strict:
- A wrapper with
form-floating. - The
inputfirst, withform-controland aplaceholderattribute. - The
labelafter the input.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Floating labels</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container py-4" style="max-width: 420px">
<h1 class="h4 mb-3">Log in to Pune Library</h1>
<form>
<div class="form-floating mb-3">
<input type="email" class="form-control" id="loginEmail" placeholder="name@example.com" autocomplete="email">
<label for="loginEmail">Email address</label>
</div>
<div class="form-floating mb-3">
<input type="password" class="form-control" id="loginPass" placeholder="Password" autocomplete="current-password">
<label for="loginPass">Password</label>
</div>
<div class="form-floating mb-3">
<select class="form-select" id="branch">
<option>Shivajinagar</option>
<option>Kothrud</option>
<option>Viman Nagar</option>
</select>
<label for="branch">Home branch</label>
</div>
<div class="form-floating mb-3">
<textarea class="form-control" id="note" placeholder="Note" style="height: 100px"></textarea>
<label for="note">Note for the librarian (optional)</label>
</div>
<div class="input-group mb-3">
<span class="input-group-text">+91</span>
<div class="form-floating">
<input type="tel" class="form-control" id="phone" placeholder="Phone">
<label for="phone">Mobile for OTP</label>
</div>
</div>
<button class="btn btn-primary w-100 py-2" type="submit">Log in</button>
</form>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>Why the rules matter:
- Input before label. Bootstrap's CSS uses the "next sibling" selector (
input ~ label) to move the label when the input changes. If the label comes first, CSS cannot reach it. - A placeholder is required. The CSS checks
:placeholder-shownto know whether the field is empty. The placeholder text itself is never shown, because the label covers it, so any text works. - Textareas get a fixed height with
style="height: ...", since therowsattribute is not used here. - Selects always show their label in the floated position, because a select always has a value.
- A
form-floatingblock can go inside an input group, as the mobile number field shows.
Floating labels suit short forms like login and sign-up. For long forms with help text under every field, normal labels above the fields are usually clearer.
Common mistakes
- Label before input in
form-floating. The label never moves and overlaps the text. Put the input first. - Missing placeholder in a floating field. The label overlaps the value after typing. Add any placeholder text.
- Keeping Bootstrap 4's
input-group-append. It is not needed in Bootstrap 5 and can break the rounded corners. - Unlabelled fields inside groups. A ₹ sign is not a label. Add a
<label>oraria-labelto every input.
Key points
input-groupjoins inputs withinput-group-textadd-ons, buttons, selects and dropdowns into one control.- Link meaningful add-ons with
aria-describedby, and label every field. - Use
input-group-sm,input-group-lgandflex-nowrapto size and control wrapping. - A floating label needs
form-floating, then the input with aplaceholder, then thelabel. - Bootstrap 5 dropped
input-group-prependandinput-group-append; add-ons go straight into the group.
Exercise
Create a "Send money" form for a college canteen wallet app. Use floating labels for the receiver's UPI ID and a note. For the amount, use an input group with "₹" before the field and ".00" after it. Below it, add an input group with a text field for a promo code and a "Check" button. Finish with a full-width "Pay" button.
Show solution
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Send money</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container py-4" style="max-width: 420px">
<form>
<div class="form-floating mb-3">
<input type="text" class="form-control" id="upi" placeholder="UPI ID">
<label for="upi">Receiver's UPI ID</label>
</div>
<label for="amount" class="form-label">Amount</label>
<div class="input-group mb-3">
<span class="input-group-text">₹</span>
<input type="number" class="form-control" id="amount" min="1">
<span class="input-group-text">.00</span>
</div>
<div class="form-floating mb-3">
<input type="text" class="form-control" id="note" placeholder="Note">
<label for="note">Note (for example, lunch with Farhan)</label>
</div>
<div class="input-group mb-3">
<input type="text" class="form-control" aria-label="Promo code" placeholder="Promo code">
<button class="btn btn-outline-secondary" type="button">Check</button>
</div>
<button type="submit" class="btn btn-success w-100">Pay</button>
</form>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>Each floating field has the input first, a placeholder, then the label. The amount group puts input-group-text add-ons on both sides of the number field, and the promo code field has an aria-label because it has no visible label.
Quick Quiz
1. Which class wraps the ₹ sign in an input group?
In Bootstrap 5, text add-ons use input-group-text directly inside input-group.
2. In a form-floating block, what must come first?
CSS moves the label using a selector that only works when the label comes after the input.
3. Why does a floating label input need a placeholder even though it is never shown?
The placeholder lets CSS know when the field is empty so the label can sit inside it.
4. An input group wraps onto two lines on a small phone. Which class keeps it on one line?
Input groups are flex containers, and flex-nowrap stops their items from wrapping.
Learn Bootstrap with a mentor
Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
