Today we’ll learn how to add “deep linking” to the Bootstrap 4 tabs. To better understand what we’re working towards, check out the demo page and pay attention to two things:
- How the URL changes as you click on the tabs.
- By giving each of the tabs its own URL, the content becomes shareable. If you grab the URL of a tab and open it in another browser/window the corresponding tab will be visible.

We’ll outline three main steps; the HTML, the CSS, and the JavaScript. A certain level of competency for all three of these is assumed. Excited to see how we’ll build this? If the answer is yes, let’s get started!
1. Building the Tabs
We’ll kick things off with a starter template taken from Bootstrap’s documentation.
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous"> <!-- custom CSS --> <link rel="stylesheet" href="css/main.css"> <title>How to Add Deep Linking to the Bootstrap 4 Tabs Component</title> </head> <body> <!-- content here --> <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script> <!-- custom JS --> <script src="js/main.js"></script> </body> </html>
To create the tabs, we’ll take advantage of example code for the tab component that Bootstrap provides:
<ul class="nav nav-mytabs" id="myTab" role="tablist"> <li class="nav-item"> <a class="nav-link active" id="home-tab" data-toggle="tab" href="#home" role="tab" aria-controls="home" aria-selected="true">Home</a> </li> <li class="nav-item"> <a class="nav-link" id="history-tab" data-toggle="tab" href="#history" role="tab" aria-controls="history" aria-selected="false">History</a> </li> <li class="nav-item"> <a class="nav-link" id="city-attractions-tab" data-toggle="tab" href="#city-attractions" role="tab" aria-controls="city-attractions" aria-selected="false">City Attractions</a> </li> </ul> <div class="tab-content mytab-content" id="myTabContent"> <div class="tab-pane fade show active" id="home" role="tabpanel" aria-labelledby="home-tab"> <!-- content here --> </div> <div class="tab-pane fade" id="history" role="tabpanel" aria-labelledby="history-tab"> <!-- content here --> </div> <div class="tab-pane fade" id="city-attractions" role="tabpanel" aria-labelledby="city-attractions-tab"> <!-- content here --> </div> </div>
2. The CSS
Next we specify a few CSS rules for our component. Nothing too fancy, just some basic styles. It’s worth mentioning that in a previous tutorial, we used similar styles to build a custom tab component.
Here are the initial styles:
.nav-mytabs { margin-top: 2rem; } .nav-mytabs li:not(:last-child) { margin-right: 7px; } .nav-mytabs a { position: relative; top: 4px; padding: 10px 25px; border-radius: 2px 2px 0 0; background: white; color: black; opacity: 0.7; transition: all 0.1s ease-in-out; } .nav-mytabs a.active, .nav-mytabs a:hover { opacity: 1; top: 0; } .mytab-content { position: relative; z-index: 2; padding: 25px; border-radius: 0 4px 4px 4px; background: white; }
3. The JavaScript
With the HTML and CSS in place, it’s time to look at the required JavaScript code (this is the important bit).
First, when the DOM is ready, we retrieve the page URL and use a regular expression to remove its trailing slash. For instance, if the original URL is something/
, the modified URL will be something
.
Next, we check to see if the URL contains a hash. If that is the case, it means we want to display the contents of the second or third tab (in our example). To do so, we carry out the following:
- Retrieve the name of the target tab and activate it by using Bootstrap’s
tab
method. - Use a regular expression to generate the desired URL format.
- Update the page URL without forcing a page reload by taking advantage of the
replaceState
method. - Optionally, force the page scroll to start from the top of the page.
Each time we click on a tab we do the following:
- Retrieve the
href
attribute value of this tab. In our case, the possible values are#home
,#history
,#city-attractions
. - Check the attribute value and, depending on that, build the desired URL format.
- Update the page URL without forcing a page reload by taking advantage of the
replaceState
method.
Here’s the JavaScript which takes care of all that:
$(document).ready(() => { let url = location.href.replace(/\/$/, ""); if (location.hash) { const hash = url.split("#"); $('#myTab a[href="#'+hash[1]+'"]').tab("show"); url = location.href.replace(/\/#/, "#"); history.replaceState(null, null, url); setTimeout(() => { $(window).scrollTop(0); }, 400); } $('a[data-toggle="tab"]').on("click", function() { let newUrl; const hash = $(this).attr("href"); if(hash == "#home") { newUrl = url.split("#")[0]; } else { newUrl = url.split("#")[0] + hash; } newUrl += "/"; history.replaceState(null, null, newUrl); }); });
4. Browser Support
The demo should work well in all recent browsers. For simplicity, I haven’t used a JavaScript compiler (e.g. Babel), but in your own code you might have to.
Conclusion
In this tutorial, we managed to customize the behavior of the Bootstrap 4 tabs, by giving each tab an identifiable URL, making their content more navigable and shareable. Keep in mind that the process implemented here can also be applied to any other framework or even a custom tab component.
If there’s anything you didn’t understand, or anything seems unclear to you, let me know in the comments below!
Further Reading
Subscribe below and we’ll send you a weekly email summary of all new Web Design tutorials. Never miss out on learning about the next big thing.
Update me weeklyEnvato Tuts+ tutorials are translated into other languages by our community members—you can be involved too!
Translate this post