The Utility API
|
This section documents Bootstrap 5.x as implemented by the official Bootstrap project. No specific patch version is pinned. Unlike the other reference sections on this site, no single reference book underpins it: the content was generated with the assistance of AI from general knowledge of Bootstrap, and should be verified against the current official documentation at getbootstrap.com/docs before relying on it in production. This section’s bibliography lists the reference material consulted while preparing these pages. |
Every class covered in Utilities — mt-3, text-primary, d-flex,
shadow-sm — is not hand-written CSS sitting in a Bootstrap source file. It is generated at Sass-compile
time from a single configuration structure: the $utilities Sass map. This page covers that generation
mechanism, referred to in Bootstrap’s own docs as the Utility API, and how to hook into it to add, change, or
remove utility classes without patching Bootstrap’s source.
The map itself is ordinary Sass data — see Lists & Maps for the map.merge,
map.get, and nested-map mechanics this page relies on, and Functions for how a
value computed from a map can flow into a property.
The shape of $utilities
$utilities is a map whose keys are human-readable utility names and whose values are themselves maps
describing how to generate that utility’s classes. A trimmed real entry looks like this:
$utilities: (
"margin-top": (
property: margin-top,
class: mt,
responsive: true,
values: map.merge($spacers, (auto: auto))
),
"display": (
property: display,
responsive: true,
print: true,
class: d,
values: none inline inline-block block grid table table-row table-cell flex inline-flex
)
);
The inner map’s keys are read by the Sass loop that walks $utilities (part of Bootstrap’s own
scss/_utilities.scss / scss/mixins/_utilities.scss) and control the generated output:
| Key | Effect |
|---|---|
|
The CSS property (or list of properties) the utility sets. |
|
The class-name prefix, e.g. |
|
A list or map of values to generate one class per value. A map’s keys become the class-name suffix
( |
|
When |
|
A list of pseudo-class variants to also generate, e.g. |
|
When |
|
Opts the utility into Bootstrap’s RFS (responsive font size) fluid-scaling mechanism. |
Compiling this map is what produces the flat list of classes documented on
Utilities — one Sass @each loop over $utilities, and a nested loop over
each entry’s values, in place of hundreds of manually written rules.
Extending the map with map-merge
Bootstrap’s own $utilities map is declared with !default inside bootstrap/scss/_utilities.scss
($utilities: () !default; $utilities: map-merge((…), $utilities);) — so, unlike a plain !default variable
(see Customization), it does not exist as a Sass variable at all until
that partial has been @import-ed. Extending it is therefore an after-import map-merge(), layering project
additions over Bootstrap’s already-loaded defaults, run after utilities but before utilities/api (the
partial that reads $utilities and actually generates classes from it):
@import "bootstrap/scss/functions";
@import "bootstrap/scss/variables";
@import "bootstrap/scss/variables-dark";
@import "bootstrap/scss/maps";
@import "bootstrap/scss/mixins";
@import "bootstrap/scss/utilities"; // declares Bootstrap's own default $utilities map
$utilities: map-merge(
$utilities,
(
"cursor": (
property: cursor,
class: cursor,
values: auto pointer grab not-allowed
),
"letter-spacing": (
property: letter-spacing,
class: ls,
values: (
1: 0.05em,
2: 0.1em,
3: 0.2em
)
)
)
);
@import "bootstrap/scss/utilities/api"; // generates classes from the merged map
This adds two brand-new utility groups — cursor-pointer, ls-1, ls-2, ls-3, and so on — generated by
the same mechanism, and therefore consistent in naming and output style with every built-in utility.
Modifying an existing utility
Because map.merge performs a key-by-key merge one level deep by default, merging a map keyed by an
existing utility name (e.g. "font-size") replaces that entry’s inner map wholesale rather than combining
individual sub-keys. To change just one aspect — say, add a state: hover variant to "text-color" — merge
at the inner-map level too:
@use "sass:map";
$utilities: map.merge(
$utilities,
(
"text-color": map.merge(
map.get($utilities, "text-color"),
(state: hover)
)
)
);
map.get($utilities, "text-color") reads Bootstrap’s existing entry, and the inner map.merge layers
state: hover on top of it, preserving every other key (property, class, values) that was already there.
Removing a utility
Setting an entry’s value to null removes it from the generated output entirely — this is how a project sheds
utilities it never uses, to reduce the compiled CSS’s size:
@use "sass:map";
$utilities: map.merge(
$utilities,
(
"float": null,
"text-decoration": null
)
);
Combined with only @use-ing the specific Bootstrap component partials a project actually needs (rather than
the monolithic bootstrap entry point), pruning unused utility groups is one of the more effective ways to
control final bundle size — see Customization for the compilation side
of that trade-off.
Generation pipeline
$utilities map"] OVERRIDES["Project overrides
map.merge(...)"] end MERGED["Final $utilities map"] LOOP["utilities/api.scss
@each loop over the map"] CSS["Generated utility classes
.mt-3, .d-md-flex, .cursor-pointer, ..."] DEFAULTS --> MERGED OVERRIDES --> MERGED MERGED --> LOOP LOOP --> CSS classDef core fill:#3f51b5,stroke:#1a237e,color:#fff class LOOP core
Nothing about this pipeline is special-cased for Bootstrap’s own utilities versus a project’s additions — a
custom entry in $utilities is walked by the exact same loop, with the exact same responsive/state/print
options available, that produces mt-3 or d-flex. This is why the Utility API is the recommended way to add
new single-purpose classes to a Bootstrap project instead of hand-writing standalone CSS rules alongside it.
Ordering matters
The @import statements in the extension example above must appear in this exact order: Bootstrap’s
functions, variables, variables-dark, maps, and mixins partials first, then utilities — the partial
that declares $utilities in the first place, so it must run before anything can merge into it — then the
project’s own map-merge() reassignment, and only then utilities/api (the partial that loops over
$utilities and generates classes from it). Merging before utilities has been @import-ed is not a silent
no-op, it is a hard Undefined variable compile error, since $utilities does not exist yet. Loading
utilities/api before the merge, on the other hand, is the silent mistake — it generates the unmodified
default set, with the override having no effect — and is one of the most common mistakes when first customizing
the Utility API.