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

property

The CSS property (or list of properties) the utility sets.

class

The class-name prefix, e.g. mt for margin-top. If omitted, the property name itself is used.

values

A list or map of values to generate one class per value. A map’s keys become the class-name suffix (mt-0, mt-3, mt-auto) while its values become the CSS value; a plain list uses each value as both.

responsive

When true, also generates a breakpoint-infixed class per value per breakpoint (mt-md-3, d-lg-none, …).

state

A list of pseudo-class variants to also generate, e.g. state: hover produces a separate, opt-in .mt-3-hover:hover rule — not a :hover rule on .mt-3 itself — so existing uses of the base class are unaffected (used sparingly — mostly for .text- and .link- utilities).

print

When true, also generates a @media print variant (d-print-none).

rfs

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

flowchart LR subgraph config["Sass configuration"] DEFAULTS["Bootstrap's default
$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.