Extending Bootstrap with the Utility API

Advanced
12 min

Extending Bootstrap with the Utility API

Every utility class in Bootstrap (d-flex, mt-3, text-center, w-50) is generated from one Sass map called $utilities. The utility API lets you read and modify that map before compilation: add new utilities, make existing ones responsive, add hover or focus variants, rename classes, or delete groups you never use. After this lesson you will be able to generate exactly the utility set your project needs and nothing more.

Anatomy of a Utility

Each entry in $utilities is a map with a small set of keys:

scss
"opacity": ( property: opacity, class: opacity, state: hover, responsive: false, values: ( 0: 0, 25: .25, 50: .5, 75: .75, 100: 1 ) )

| Key | Purpose | |---|---| | property | CSS property (or list of properties) to set | | class | Class prefix; defaults to the property name | | values | Map of suffix to value, or a plain list where suffix equals value | | responsive | Generate {class}-{bp}-{suffix} variants | | state | Generate pseudo-class variants such as hover or focus | | print | Generate {class}-print-{suffix} | | rfs | Apply responsive font sizing to values | | css-var / css-variable-name | Emit a CSS variable instead of a property | | local-vars | Additional CSS variables to set on the rule | | rtl | Set to false to skip the utility in RTL builds |

The example above generates .opacity-0, .opacity-25, .opacity-50 and, because of state: hover, .opacity-50-hover and friends.

Required Import Order

The API only works when you import Bootstrap's parts in order so that $utilities exists before you modify it:

scss
// 1. Functions first (needed to manipulate colors and maps) @import "bootstrap/scss/functions"; // 2. Variable overrides go here $primary: #4f46e5; // 3. Core variables, maps, mixins and the default $utilities map @import "bootstrap/scss/variables"; @import "bootstrap/scss/variables-dark"; @import "bootstrap/scss/maps"; @import "bootstrap/scss/mixins"; @import "bootstrap/scss/utilities"; // 4. Modify $utilities here // 5. Layout, components and the utilities API that compiles the map @import "bootstrap/scss/root"; @import "bootstrap/scss/reboot"; // ... other parts you need @import "bootstrap/scss/helpers"; @import "bootstrap/scss/utilities/api";

The last import, utilities/api, is what actually generates the classes. Importing bootstrap/scss/bootstrap in one line also works, as in the sample at the top, as long as your modifications come before it and after utilities.

Adding a New Utility

scss
$utilities: map-merge( $utilities, ( "cursor": ( property: cursor, class: cursor, values: auto pointer grab not-allowed ), "letter-spacing": ( property: letter-spacing, class: ls, values: ( tight: -0.02em, normal: 0, wide: 0.08em ) ) ) );

Output includes .cursor-pointer, .cursor-grab, .ls-tight and .ls-wide.

Modifying an Existing Utility

Use map-get to fetch the existing definition and map-merge to change only some keys. Making width responsive is the classic example:

scss
$utilities: map-merge( $utilities, ( "width": map-merge( map-get($utilities, "width"), ( responsive: true ) ) ) );

Now .w-md-50, .w-lg-25 and so on exist. Add values in the same way:

scss
"width": map-merge( map-get($utilities, "width"), ( values: map-merge( map-get(map-get($utilities, "width"), "values"), ( 10: 10%, 90: 90% ) ) ) )

Hover and Focus Variants

scss
"shadow": map-merge( map-get($utilities, "shadow"), ( state: hover focus ) )

This produces .shadow-hover:hover and .shadow-focus:focus alongside the normal classes, so a card can lift on hover with class="card shadow-sm shadow-hover".

Renaming and Removing

Change the class key to rename:

scss
"margin-start": map-merge( map-get($utilities, "margin-start"), ( class: ml ) // restores Bootstrap 4's ml-* naming )

Set a utility to null to remove it entirely, which shrinks the CSS:

scss
$utilities: map-merge( $utilities, ( "float": null, "user-select": null, "pointer-events": null ) );

You can also remove one value by merging a values map that omits it.

Generating Utilities in Your Own Selector

The generate-utility mixin emits a utility's rules anywhere, including inside a wrapper selector or container query:

scss
.dark-panel { @include generate-utility(map-get($utilities, "opacity"), "", false); }

This is an advanced tool; most projects only need map-merge.

Common Mistakes

  • Modifying $utilities before importing utilities. The map does not exist yet, so map-get returns null and compilation fails.
  • Modifying it after utilities/api. The classes are already generated; changes have no effect.
  • Expecting responsive variants by default. Only some utilities (display, flex, text alignment, spacing) are responsive; set responsive: true explicitly.
  • Nesting maps incorrectly. values must be a map or list; a string produces a single class named after the string.
Quick Quiz
Question 1 of 3

Which import must come before you modify `$utilities`?

Key Takeaways

  • All utilities are generated from the $utilities Sass map; each entry declares property, class, values and optional responsive, state, print and rtl keys.
  • Modify the map after importing bootstrap/scss/utilities and before bootstrap/scss/utilities/api.
  • map-merge adds new utilities or changes existing ones, such as making width responsive or adding hover states.
  • Set entries to null to remove unused utilities and shrink the CSS.
  • generate-utility can emit utilities inside custom selectors for advanced cases.

Next lesson: The JavaScript API: Instances, Options and Events - use every plugin programmatically, pass options, listen to lifecycle events and avoid duplicate instances.

Extending Bootstrap with the Utility API - Bootstrap | CodeYourCraft | CodeYourCraft