Form Controls
Every Bootstrap 5 form starts with the same handful of control classes: .form-control for
text-style inputs and textareas, .form-select for dropdowns, and .form-check
for checkboxes and radios. This lesson covers each control on its own, plus sizing and disabled/readonly
states, using the fields from TrailMap's "Submit a Trail" form as the running example: trail name,
description, difficulty, distance, region, a photo upload, and a terms checkbox.
Text Inputs and Textareas: .form-control
.form-control is the base class for nearly every text-style input: text,
email, number, password, search, plus
<textarea>. It gives the field Bootstrap's border, padding, and focus ring — you
don't need any extra wrapper class for a plain text field.
<!-- TrailMap: trail name (text input) -->
<label for="trailName" class="form-label">Trail Name</label>
<input type="text" class="form-control" id="trailName" placeholder="e.g. Eagle Ridge Loop">
<!-- TrailMap: description (textarea) -->
<label for="trailDescription" class="form-label">Description</label>
<textarea class="form-control" id="trailDescription" rows="4" placeholder="What makes this trail worth hiking?"></textarea>
Select Menus: .form-select
.form-select is the Bootstrap 5 class for dropdowns. It replaces the old Bootstrap 4
.custom-select class (and the unstyled plain <select>) with a single
control that gets the custom arrow icon and consistent sizing automatically — you no longer add any
"custom" prefix at all.
<!-- TrailMap: difficulty -->
<label for="trailDifficulty" class="form-label">Difficulty</label>
<select class="form-select" id="trailDifficulty">
<option selected disabled value="">Choose a difficulty</option>
<option value="easy">Easy</option>
<option value="moderate">Moderate</option>
<option value="hard">Hard</option>
<option value="strenuous">Strenuous</option>
</select>
.custom-select to .form-select. Plain, unstyled <select> elements from Bootstrap 3/4 markup also need .form-select added — Bootstrap 5 no longer styles a bare select by default.Checkboxes and Radios: .form-check
Checkboxes and radios use a three-part pattern: a .form-check wrapper div, a
.form-check-input on the <input> itself, and a
.form-check-label on the paired <label>. Add .form-check-inline
to the wrapper to lay a group out on one line instead of stacked.
<!-- TrailMap: trail type (radios) -->
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="trailType" id="typeLoop" value="loop" checked>
<label class="form-check-label" for="typeLoop">Loop</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="trailType" id="typeOutBack" value="out-and-back">
<label class="form-check-label" for="typeOutBack">Out-and-back</label>
</div>
<!-- TrailMap: agree to terms (checkbox) -->
<div class="form-check">
<input class="form-check-input" type="checkbox" id="agreeTerms">
<label class="form-check-label" for="agreeTerms">I confirm this trail information is accurate</label>
</div>
Sizing Controls: .form-control-lg and .form-control-sm
Add .form-control-lg or .form-control-sm next to .form-control
for a taller or shorter field. Selects use the matching .form-select-lg /
.form-select-sm. There's no "medium" modifier — the plain class is the medium size.
<!-- TrailMap: distance input at three sizes -->
<input type="number" class="form-control form-control-lg mb-2" placeholder="Distance (large)">
<input type="number" class="form-control mb-2" placeholder="Distance (default)">
<input type="number" class="form-control form-control-sm" placeholder="Distance (small)">
Disabled and Readonly States
The disabled attribute greys the field out, blocks interaction, and stops its value
from being submitted with the form. readonly also blocks editing, but the field still
looks normal and its value is still submitted — useful for a value the user shouldn't change but the
server still needs, like an auto-generated trail ID.
<!-- Disabled: can't be edited, value is NOT submitted -->
<select class="form-select" disabled>
<option>Region locked while trail is under review</option>
</select>
<!-- Readonly: can't be edited, value IS submitted -->
<input type="text" class="form-control" value="TRAIL-00482" readonly>
Worked Example: Trail Name, Description, and Difficulty Together
The three controls from this lesson, combined the way they'd actually appear at the top of TrailMap's "Submit a Trail" form:
<form>
<div class="mb-3">
<label for="trailName" class="form-label">Trail Name</label>
<input type="text" class="form-control" id="trailName">
</div>
<div class="mb-3">
<label for="trailDescription" class="form-label">Description</label>
<textarea class="form-control" id="trailDescription" rows="3"></textarea>
</div>
<div class="mb-3">
<label for="trailDifficulty" class="form-label">Difficulty</label>
<select class="form-select" id="trailDifficulty">
<option value="easy">Easy</option>
<option value="moderate">Moderate</option>
<option value="hard">Hard</option>
</select>
</div>
</form>
Notice the .mb-3 wrapping each control instead of any special "form group" class — that's exactly what the next lesson is about.