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.

Note on this site: Codingwithsonu.com itself is built with Bootstrap 3. This track teaches Bootstrap 5 as its own independent subject, using its own separate class names — the two don't mix.

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>
BS4 → BS5 change: If you're porting an older form, rename any .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.

Next up: These controls are only half the story — spacing them, lining them up in a grid, and building inline forms is covered in Form Layout next.