Form Layout
Knowing the control classes from the last lesson only gets you individual fields — a real form also needs spacing between them and, often, a grid to line labels up with inputs. Bootstrap 5 gives you three layout patterns: plain stacked spacing, a horizontal grid layout, and inline flex layouts. This lesson builds all three, finishing with the complete TrailMap "Submit a Trail" form laid out horizontally.
Stacked Layout: .mb-3 Replaces .form-group
In Bootstrap 3 and 4, every field was wrapped in a <div class="form-group"> to
get consistent spacing below it. Bootstrap 5 removed .form-group entirely.
The replacement isn't a new class with a similar name — it's just the plain spacing utility
.mb-3 (margin-bottom, step 3) applied directly to the wrapper. One less special-purpose
class to remember, and it composes with every other spacing utility you already know.
<!-- Bootstrap 4 (old) -->
<div class="form-group">
<label>Region</label>
<input type="text" class="form-control">
</div>
<!-- Bootstrap 5 (current) -->
<div class="mb-3">
<label class="form-label">Region</label>
<input type="text" class="form-control">
</div>
.form-group in Bootstrap 5 — searching your CSS or a component library for it and finding nothing is expected, not a bug. Swap it for .mb-3 (or whatever spacing value fits your layout) and add the new .form-label class to labels.Horizontal Layout: Grid Rows, Columns, and .col-form-label
For label-beside-input rows, reuse the grid: a .row containing a label column and an
input column. Add .col-form-label to the label so its vertical padding lines up with the
input's height instead of sitting flush against the top.
<div class="row mb-3">
<label for="trailRegion" class="col-sm-2 col-form-label">Region</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="trailRegion" placeholder="e.g. Blue Ridge Mountains">
</div>
</div>
Inline Forms: Flex and Gap Utilities
Bootstrap 5 dropped the dedicated .form-inline class from Bootstrap 4. To lay controls
out on one line now, reach for the same flexbox utilities you'd use anywhere else: .d-flex
on the container, .gap-2 for consistent spacing between items, and
.align-items-center to keep labels and inputs vertically centered.
<!-- A compact inline trail search + region filter -->
<form class="d-flex align-items-center gap-2">
<label class="visually-hidden" for="quickSearch">Search trails</label>
<input type="search" class="form-control" id="quickSearch" placeholder="Search trails...">
<button type="submit" class="btn btn-primary">Search</button>
</form>
Worked Example: The Full TrailMap "Submit a Trail" Form
Putting stacked spacing and the horizontal grid together — TrailMap's real submission form, with every field type from the previous lesson laid out label-left, input-right:
<form>
<div class="row mb-3">
<label for="trailName" class="col-sm-3 col-form-label">Trail Name</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="trailName">
</div>
</div>
<div class="row mb-3">
<label for="trailDescription" class="col-sm-3 col-form-label">Description</label>
<div class="col-sm-9">
<textarea class="form-control" id="trailDescription" rows="3"></textarea>
</div>
</div>
<div class="row mb-3">
<label for="trailDifficulty" class="col-sm-3 col-form-label">Difficulty</label>
<div class="col-sm-9">
<select class="form-select" id="trailDifficulty">
<option value="easy">Easy</option>
<option value="moderate">Moderate</option>
<option value="hard">Hard</option>
</select>
</div>
</div>
<div class="row mb-3">
<label for="trailDistance" class="col-sm-3 col-form-label">Distance (mi)</label>
<div class="col-sm-9">
<input type="number" class="form-control" id="trailDistance" step="0.1" min="0">
</div>
</div>
<div class="row mb-3">
<label for="trailRegion" class="col-sm-3 col-form-label">Region</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="trailRegion">
</div>
</div>
<div class="row mb-3">
<label for="trailPhoto" class="col-sm-3 col-form-label">Photo</label>
<div class="col-sm-9">
<input type="file" class="form-control" id="trailPhoto" accept="image/*">
</div>
</div>
<div class="row mb-3">
<div class="col-sm-9 offset-sm-3">
<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>
</div>
</div>
<button type="submit" class="btn btn-primary">Submit Trail</button>
</form>
Every row follows the same shape: a .col-form-label for the label, a matching-width
column for the control, wrapped in .row.mb-3 for spacing between rows.