The cleanest way to add Bootstrap to WordPress is to enqueue its CSS and JavaScript bundle from a child theme’s functions.php with wp_enqueue_style() and wp_enqueue_script(). WordPress doesn’t ship Bootstrap. The framework only loads when a theme, plugin or starter theme asks for it.

Once it’s in, templates get a responsive grid, ready-made components and utility classes. Guides from KnownHost and DEV Community still paste link tags into header.php, which the WordPress Theme Handbook argues against.

The files can come from the jsDelivr CDN or from the theme folder. Your Bootstrap version decides whether jQuery or Popper has to load alongside them.

Which Method Should You Use to Add Bootstrap to WordPress?

If the site already has a theme you want to keep, enqueue Bootstrap from a child theme. A Bootstrap-based starter theme only makes sense when you’re building from scratch.

Every method ends up doing the same job, which is getting Bootstrap’s CSS and JavaScript onto the page, because WordPress doesn’t include them. What changes is who controls the files and what survives an update.

MethodSetup effortSurvives theme updatesBest fit
Enqueue local filesDownload, upload, enqueueYes, from a child themeClient sites, production
Enqueue from a CDNOne function, no downloadsYes, from a child themePrototypes, quick builds
Bootstrap pluginInstall and activateFollows the plugin’s updatesSites where nobody edits theme files
Bootstrap-based themeInstall and activateYes, with a child themeNew builds from scratch

Bootstrap brings its own grid system, components and utility classes. That’s why a theme gets responsive layouts without hand-written media queries.

On an existing custom theme, enqueue from a child theme and move on. A new build is better served by a Bootstrap-based starter theme with a child theme on top. If you can’t touch code at all, a plugin will do it, though its release schedule becomes yours. And for a throwaway prototype, a CDN link inside the enqueue function is plenty.

KnownHost and DEV Community both tell readers to paste link tags into header.php. The WordPress Theme Handbook says the opposite. It wants stylesheets and scripts loaded through wp_enqueue_style() and wp_enqueue_script(), so the theme and every plugin share one loading system.

Where Should the Bootstrap Code Go in a WordPress Theme?

In the functions.php file of a child theme. Anything you edit in a parent theme is overwritten when that theme updates, and the Bootstrap code goes with it.

Child theme vs parent theme

According to the WordPress Theme Handbook, a child theme includes everything from its parent and keeps your changes while the parent still receives updates. A parent theme gives you no such protection, since any edit sits in files the next update replaces.

A child theme needs just one file to exist, and a second one to load Bootstrap. style.css is the required file, with a theme header whose Template field names the parent. functions.php holds the enqueue code.

Understrap’s child theme starter shows the pattern in practice. Its functions.php dequeues the parent’s style and script handles, then enqueues its own files from the child theme folder with get_stylesheet_directory_uri().

Take a backup before touching anything. If the site is live, read up on editing functions.php safely first.

Is responsive design still a top priority?

Explore the latest responsive design statistics: adoption rates, performance impact, user behavior, and trends shaping modern websites.

See the Numbers →

One typo in that file can end in the white screen of death. Keep FTP or file manager access open in another tab while you save.

Block themes and header.php

Block themes have no header.php to edit. Their templates and parts are HTML files built from blocks, so there’s nothing to paste a link tag into.

functions.php is still available as an optional file, though. The Theme Handbook’s child theme guide shows a block theme’s functions.php enqueueing a stylesheet with wp_enqueue_style(), which means the enqueue code below works on block themes too.

Bootstrap CDN or Local Files in WordPress

Use a CDN for prototypes and local files for client sites. The old argument for a CDN was that visitors already had Bootstrap cached from another site. That stopped holding when Chrome 86 began partitioning its HTTP cache.

Tutorials such as W3Schools still list that shared cache as a reason to use the Bootstrap CDN. Before the change, Chromium’s own preliminary experiments put the overall cache miss rate up by about 3.6% and the share of bytes loaded from the network up by about 4%, with first contentful paint moving only about 0.3%.

Put together, those numbers say cross-site sharing never moved much for a typical visitor, and Chrome no longer shares cached files across sites at all.

Bootstrap CDN

Bootstrap’s download page recommends jsDelivr and says the project’s own documentation uses it. The upside is that there’s nothing to download or keep track of in the theme folder, and editing one version string moves the site to a new release. The downside is a third-party request on a visitor’s first load, plus a dependency on that host staying reachable.

When the CDN host is blocked, the browser console logs failed to load resource errors and the page renders without Bootstrap’s styles.

The version number sits inside the URL, so jsDelivr keeps serving that release until you change the string. Several ranking tutorials still point to 4.5.2 files on maxcdn.bootstrapcdn.com (KnownHost, for one), while Bootstrap’s download page now lists jsDelivr.

The team’s release post of August 25, 2025 called 5.3.8 the last planned patch before 5.4.0. Check the current number on getbootstrap.com before pasting a URL.

Local files

Local files skip the third-party request. Download the compiled code from getbootstrap.com, upload bootstrap.min.css and bootstrap.bundle.min.js into the child theme folder, and point the enqueue calls at them.

get_stylesheet_directory_uri() . '/assets/css/bootstrap.min.css'

Use get_template_directory_uri() only when the files live in the parent theme, which brings the update problem right back.

ProsCons
Loads from your own hostManual download per release
No third-party requestFiles add weight to the theme package
You decide when versions changeYou also own the update chore

How to Enqueue Bootstrap in WordPress

Hook a function to wp_enqueue_scripts in the child theme’s functions.php. Inside it, call wp_enqueue_style() for the Bootstrap CSS and wp_enqueue_script() for the JavaScript bundle. Make the Bootstrap stylesheet a dependency of your own CSS so it loads first.

  1. Create a child theme, or activate the one you already have.
  2. Back up the site, then open the child theme’s functions.php.
  3. Paste the function below, swapping in your own prefix for mytheme.
  4. Save the file and reload a front-end page.
<?php
function mytheme_enqueue_bootstrap() {
wp_enqueue_style(
'bootstrap-css',
'https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css',
array(),
'5.3.8'
);

wp_enqueue_style(
'child-style',
get_stylesheet_uri(),
array( 'bootstrap-css' ),
wp_get_theme()->get( 'Version' )
);

wp_enqueue_script(
'bootstrap-js',
'https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js',
array(),
'5.3.8',
array(
'in_footer' => true,

'strategy'  => 'defer',
)
);
}
add_action( 'wp_enqueue_scripts', 'mytheme_enqueue_bootstrap' );

According to the WordPress developer documentation, wp_enqueue_scripts is the correct hook for front-end assets. Despite the name, it handles styles as well as scripts.

What each enqueue argument does

The handle is a unique name, like ‘bootstrap-css’ above. Other code uses it to depend on or dequeue the file, and that includes plugins.

The source is the full URL, or a path built with get_stylesheet_directory_uri() when the files are local. Dependencies are the handles that must load first, so listing ‘bootstrap-css’ puts Bootstrap ahead of the child stylesheet. The version is just a string that WordPress appends to the URL as a query string, and the last argument is an array that replaces the old $in_footer flag.

A core developer note from July 14, 2023 introduced the ‘strategy’ key for WordPress 6.3, accepting ‘defer’ or ‘async’. Defer suits the Bootstrap bundle because it runs in order once the document is parsed. Async executes as soon as the file arrives.

That timing has a catch. Inline scripts that call bootstrap.Tooltip or bootstrap.Modal run before a deferred file executes. Wrap them in a DOMContentLoaded listener, or drop the strategy and keep the bundle in the footer.

Because child-style depends on bootstrap-css, your own rules arrive second and can override Bootstrap’s defaults without !important.

Why enqueueing beats a link tag in header.php

The Theme Handbook points to wp_enqueue_style() and wp_enqueue_script() as the standard route. A link tag typed into header.php sits outside that system.

A plugin that also loads Bootstrap can’t see the tag, so the same file can end up on the page twice.

Enqueued files print where the theme calls wp_head() and wp_footer(). Delete either call from the templates and the Bootstrap files never load.

Bootstrap 4 vs Bootstrap 5: jQuery, Popper and the JavaScript Bundle

Bootstrap 5 needs no jQuery and ships Popper inside bootstrap.bundle.min.js, so one file covers every component. Bootstrap 4 is fussier. It wants jQuery loaded first, then Popper, then its own plugins, unless you use its bundle, which includes Popper but still leaves jQuery to you.

ItemBootstrap 4.6Bootstrap 5.3
jQueryRequired by the JavaScript pluginsDropped in 5.0.0
PopperPopper 1.x, separate or in the bundlePopper 2.x, in the bundle
One-file optionBundle plus jQueryBundle alone
Load orderjQuery, Popper, Bootstrap JSPopper (if separate), Bootstrap JS

Which version to load

Load Bootstrap 5 on any new build. Bootstrap’s own migration guide says 5.0.0 dropped jQuery and ended support for Internet Explorer 10 and 11, and it moved from Popper 1.x to 2.x along the way.

Bootstrap 4 is end of life. A DevExpress notice dated March 24, 2026 puts that date in 2023 and announces that its Blazor components drop Bootstrap v4 support in release v26.1.

Keep 4.x only for a theme already built on it, and budget the upgrade as template work. Bootstrap 5 removed classes such as .media, so old markup doesn’t carry over untouched.

Do you need the JavaScript at all?

Not always. The grid, utility classes and typography run on the CSS file alone.

The JavaScript becomes mandatory the moment a theme uses dropdown menus, the collapsing navbar toggler, modals, carousels, or tooltips and popovers.

Dropdowns, popovers and Bootstrap tooltips position themselves with Popper. The plain bootstrap.min.js needs Popper loaded before it, while the bundle already contains it.

Skipping the JavaScript saves one request. The first dropdown menu puts it back.

Bootstrap 4 and the jQuery that WordPress ships

WordPress registers jQuery under the handle ‘jquery’, and Bootstrap 4’s documentation says the full jQuery build works as well as the slim build used in its examples. So declare that handle as a dependency instead of loading a second copy.

wp_enqueue_script(
'bootstrap-js',
'https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.bundle.min.js',
array( 'jquery' ),
'4.6.2',
true
);

The dependency array makes WordPress print jQuery first, and the bundle brings Popper along. Pull jQuery from a CDN on top of a plugin that uses the built-in copy and you’ve got 2 versions on one page.

If the console reports a jQuery is not defined error, the bundle printed before jQuery did. Check that ‘jquery’ sits in the dependency array.

How to Turn a WordPress Menu into a Bootstrap Navbar

Pass a Bootstrap-aware walker to wp_nav_menu() through its ‘walker’ argument and put the Bootstrap nav classes in ‘menu_class’. The default output is a plain list inside a div, which Bootstrap’s navbar styles don’t recognize.

  1. Download the walker file for your Bootstrap version and place it in the child theme folder.
  2. Load it from functions.php with require_once.
  3. Add the walker to the wp_nav_menu() call in header.php.
<?php

// functions.php
require_once get_stylesheet_directory() . '/class-wp-bootstrap-navwalker.php';

// header.php, inside the navbar markup
wp_nav_menu( array(
'theme_location' => 'primary',
'container'      => false,
'menu_class'     => 'navbar-nav',
'fallback_cb'    => 'WP_Bootstrap_Navwalker::fallback',
'walker'         => new WP_Bootstrap_Navwalker(),
) );

Replace ‘primary’ with whatever menu location your theme registers. The walker formats the menu list and nothing else.

The nav element, brand link and toggler button stay as markup in header.php, and that markup is where a Bootstrap navbar gets its structure.

Matching the walker to your Bootstrap version

For Bootstrap 4, use WP Bootstrap Navwalker. Its README says version 4 of the walker is built for Bootstrap 4.

Bootstrap 5 sites should look at the Bootstrap 5 WordPress Navbar Walker by AlexWebLab. Its class is named bootstrap_5_wp_nav_menu_walker, and Bootscore adopted it in version 5.0.2.0.

Bootscore 5.0.2.0 swapped a modified Bootstrap 4 walker for the Bootstrap 5 one. Anyone with copies of the menu files in a child theme had to change the walker code by hand after updating the parent.

Bootstrap dropdown menus open only when the Bootstrap JavaScript is loaded. The WP Bootstrap Navwalker README states that the walker does not load those files for you.

Two failure patterns show up most. A bare list means the walker never ran, so check the require_once path and confirm the ‘walker’ argument sits in the call header.php actually uses.

When no menu is assigned to the location, wp_nav_menu() falls back to wp_page_menu() by default. The ‘fallback_cb’ line above swaps that for Bootstrap markup.

Bootstrap-Based Themes and Plugins as a Shortcut

A Bootstrap-based starter theme removes the enqueue work on a new build. On an existing site it costs more than it saves, because switching themes means rebuilding every template.

ThemeBootstrap version listedChild theme route
Understrap5.2 in child theme release 1.2.0Official child starter theme
BootscoreBootstrap 5, assets loaded locallybootScore Child, described as upgrade-safe
b5st5.2.3 per its READMEAuthor advises against doing all design in a child theme; build on b5st itself, then make child themes from the finished theme

Starter themes tie you to whatever Bootstrap version they ship. Understrap’s child theme release 1.1.0 moved to Bootstrap 5 and does not produce CSS or JavaScript that works with Bootstrap 4 markup, and release 1.0.1 is the last Bootstrap 4 child.

The b5st README lists Bootstrap 5.2.3, which sits behind the 5.3 line. Check which version a starter theme ships before you build on it.

Bootscore has no Customizer options, so all customization happens in code, and its README installs the theme through the WordPress theme uploader. If an upload fails, the fixes sit in this guide to theme installation errors.

The plugin route

Bootstrap plugins usually target one piece of output. Bootscore’s own bs-contact-form-7 plugin, for example, adds Bootstrap markup to Contact Form 7 forms.

Read which handles a plugin enqueues before you activate it. A plugin that loads its own Bootstrap copy is a classic cause of the duplicate-file conflicts covered next.

My rule of thumb is simple. Starting fresh, go with a starter theme plus a child theme. Working on an existing theme, enqueue by hand.

Why Does Bootstrap Break WordPress Styles, and How Do You Fix It?

Bootstrap breaks styles when it competes with theme CSS for the same elements, when a plugin loads a second copy, or when the block editor never receives it. Check load order first, then duplicate handles, then the editor.

Conflict sourceSymptomFix
Theme CSSFonts, spacing, form fields shiftOverride –bs-body-* variables, use selector order
Plugin CSSTwo copies, mismatched classeswp_dequeue_style() at a later priority
Block editorBootstrap classes show no stylingenqueue_block_assets with is_admin()

Theme CSS conflicts

You’ll notice it right away. Body text, line height, form fields or box sizing change the moment Bootstrap loads.

The culprit is Reboot, the element-level layer inside bootstrap.min.css. Bootstrap’s documentation lists a global font-family, font-weight, line-height and color on body, and border-box sizing on every element.

When two rules carry equal specificity, the one defined last wins, as the W3C wiki lesson on the cascade explains. A child stylesheet loaded after Bootstrap beats Reboot’s element selectors by position alone.

Class selectors such as .btn or .navbar need a more specific selector, and a CSS specificity calculator shows which one wins. For global type, set –bs-body-font-family and –bs-body-color in your own :root rule.

Plugin CSS conflicts

Remove the plugin’s copy, not yours.

add_action( 'wp_enqueue_scripts', 'mytheme_remove_plugin_bootstrap', 100 );

function mytheme_remove_plugin_bootstrap() {
wp_dequeue_style( 'plugin-bootstrap' );
wp_dequeue_script( 'plugin-bootstrap-js' );
}

Swap in the handles the plugin registers. The id on its link or script tag in the page source is the handle plus a -css or -js suffix.

A note on the wp_dequeue_style() reference page explains that a style must be registered before it can be dequeued, so the removal has to run later than the original enqueue. Plugins usually enqueue at the default priority of 10, so the hook above uses 100.

Mixed versions cause their own mess. A plugin on Bootstrap 4 next to a theme on Bootstrap 5 leaves markup with classes Bootstrap 5 dropped, such as .input-group-append. And with both copies loading, every rule applies twice and the later file wins.

Block editor styles

Assets enqueued on wp_enqueue_scripts reach only the front end. A block carrying Bootstrap classes looks unstyled in the editor even though the published page renders correctly.

To apply the classes themselves:

  1. Select the block.
  2. Open the Advanced panel in the block settings.
  3. Type the Bootstrap classes into the Additional CSS class(es) field, separated by spaces.

Not every block exposes that field, since a block author can opt out. To preview the styling, load the CSS through enqueue_block_assets.

add_action( 'enqueue_block_assets', 'mytheme_editor_bootstrap' );

function mytheme_editor_bootstrap() {
if ( is_admin() ) {
wp_enqueue_style(
'bootstrap-editor',
'https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css',
array(),
'5.3.8'
);
}
}

The Block Editor Handbook says that as of WordPress 6.3, assets added on enqueue_block_assets also reach the iframed editor, and a note on the hook’s reference page limits them to the editor with an is_admin() check. The enqueue_block_editor_assets hook is meant for the editor interface. In the iframed editor it only affects post content when the stylesheet targets selectors such as .editor-styles-wrapper or .wp-block, and WordPress logs a console warning, so it is the wrong tool here.

Reboot is a reset, so test headings, lists and buttons inside the editor after loading it.

Bootstrap in WordPress FAQ

How can you tell that Bootstrap loaded correctly?

Open the front end, view the page source and look for bootstrap.min.css and bootstrap.bundle.min.js. WordPress prints each tag with an id built from the handle plus a suffix, such as bootstrap-css-css for the stylesheet and bootstrap-js-js for the script. The browser’s network tab should show both files with a 200 status.

Can you use Bootstrap on a single page or post without editing theme files?

Yes, but the files still have to load. A Custom HTML block can hold a link tag and Bootstrap markup for that one page, though it skips dependency handling and loads a second copy if the theme already enqueues Bootstrap.

How do you change Bootstrap colors and spacing without editing core files?

Leave bootstrap.min.css untouched. In Bootstrap 5, override the –bs-* CSS variables in your child theme stylesheet, or compile Bootstrap from Sass with your own values for variables such as $primary.

How do you remove Bootstrap from a theme later?

Delete the enqueue calls from the child theme’s functions.php, or run wp_dequeue_style() and wp_dequeue_script() at a later priority when a parent theme or plugin loads it.

Then clean the templates, because Bootstrap classes and the navbar walker stay in the markup until you remove them.

Does Bootstrap slow down a WordPress site?

Yes, by the weight of the files it adds, which is one CSS file and, when used, one JavaScript bundle. Skipping the JavaScript on pages without dropdowns or modals cuts that cost. So does the defer strategy, and so does hosting local files.

Keeping Bootstrap Current Without Breaking the Theme

Update Bootstrap in WordPress by changing the pinned version in the child theme’s functions.php, testing on staging first and moving one release at a time. Patch releases can still regress. The April 4, 2025 hotfix 5.3.5 repaired an Autoprefixer regression that left floating form labels raised in Firefox.

Clone the site to staging, change the version string or replace the local files, then check forms, navbar dropdowns and modals.

Compiled files, from the CDN or local copies, are untouched by Bootstrap’s Sass changes. Builds compiled from source are not. The 5.3.3 notes of February 20, 2024 call the manual variables-dark.scss import the approach for v6, so a v6 release turns an update into a migration.

With the files loading cleanly, the next step is building pages from Bootstrap components.

Bogdan Sandu
Latest posts by Bogdan Sandu (see all)