Form Layout

Individual controls are only half the story — a form also needs consistent spacing between fields and a sensible arrangement of labels next to inputs. Bootstrap 4 gives you three layout tools for this: .form-group for standard stacked spacing, the grid combined with .col-form-label for horizontal (label-beside-input) forms, and .form-inline for compact single-line forms. We'll use these to lay out the full TrailMap "Submit a Trail" form.

Bootstrap 4-only pattern: .form-group was removed entirely in Bootstrap 5 — a genuine structural change, not just a rename. Bootstrap 5 dropped the dedicated wrapper class in favor of just applying the spacing utility .mb-3 directly to whatever wraps a label+input pair. If you're maintaining a Bootstrap 4 codebase, every .form-group you see needs to become a spacing decision (usually .mb-3) when it's eventually migrated.

Standard Spacing: .form-group

Wrap each label+input pair in a .form-group <div> to get consistent bottom margin between fields, without having to think about spacing on a field-by-field basis.

<div class="form-group">
  <label for="trailName">Trail name</label>
  <input type="text" class="form-control" id="trailName">
</div>
<div class="form-group">
  <label for="trailDescription">Description</label>
  <textarea class="form-control" id="trailDescription" rows="3"></textarea>
</div>

Horizontal Forms: Grid + .col-form-label

For a label-beside-input layout, wrap the pair in .form-group.row and split it into grid columns — a small column for the label using .col-form-label (which vertically centers the label text against the input's height), and a larger column for the control.

<div class="form-group row">
  <label for="trailDistance" class="col-sm-3 col-form-label">Distance (km)</label>
  <div class="col-sm-9">
    <input type="number" class="form-control" id="trailDistance" step="0.1" min="0">
  </div>
</div>

Inline Forms: .form-inline

Add .form-inline to the <form> element to lay all its controls out on a single line (flex row) instead of stacking them — useful for compact forms like a quick trail search bar, but too cramped for a full multi-field submission form.

<form class="form-inline">
  <label class="mr-2" for="quickSearch">Search</label>
  <input type="text" class="form-control mr-2" id="quickSearch" placeholder="Trail name...">
  <button type="submit" class="btn btn-primary">Search</button>
</form>

Worked Example: Full TrailMap "Submit a Trail" Form

Putting the pieces together — every field wrapped in .form-group, with a horizontal row for distance so it sits neatly beside the difficulty select.

<!-- TrailMap: Submit a Trail — full layout with .form-group -->
<form>
  <div class="form-group">
    <label for="trailName">Trail name</label>
    <input type="text" class="form-control" id="trailName" name="trail_name">
  </div>

  <div class="form-group">
    <label for="trailDescription">Description</label>
    <textarea class="form-control" id="trailDescription" name="description" rows="4"></textarea>
  </div>

  <div class="form-group row">
    <label for="trailDifficulty" class="col-sm-3 col-form-label">Difficulty</label>
    <div class="col-sm-9">
      <select class="custom-select" id="trailDifficulty" name="difficulty">
        <option value="easy">Easy</option>
        <option value="moderate">Moderate</option>
        <option value="hard">Hard</option>
      </select>
    </div>
  </div>

  <div class="form-group row">
    <label for="trailDistance" class="col-sm-3 col-form-label">Distance (km)</label>
    <div class="col-sm-9">
      <input type="number" class="form-control" id="trailDistance" name="distance" step="0.1" min="0">
    </div>
  </div>

  <div class="form-group">
    <label for="trailRegion">Region</label>
    <select class="form-control" id="trailRegion" name="region">
      <option>Pacific Northwest</option>
      <option>Rocky Mountains</option>
      <option>Appalachian</option>
    </select>
  </div>

  <div class="form-group">
    <label for="trailPhoto">Trail photo</label>
    <div class="custom-file">
      <input type="file" class="custom-file-input" id="trailPhoto" name="photo" accept="image/*">
      <label class="custom-file-label" for="trailPhoto">Choose a photo...</label>
    </div>
  </div>

  <div class="form-check mb-3">
    <input class="form-check-input" type="checkbox" id="agreeTerms" name="agree_terms" required>
    <label class="form-check-label" for="agreeTerms">I agree to TrailMap's submission terms</label>
  </div>

  <button type="submit" class="btn btn-primary">Submit Trail</button>
</form>
Up next: Form Validation adds visual pass/fail feedback to this same form using HTML5 constraints and Bootstrap's .was-validated class.