Does Bootstrap include a timeline component?
No. The v5.3 documentation (v5.3.8 as of October 2026) lists 24 components, from Accordion to Tooltips, and a timeline isn’t among them.
What gets called a Bootstrap timeline is a custom pattern. It’s assembled from the grid, cards or a list group, utility classes and a small block of custom CSS, and it leans on existing Bootstrap components, so nothing needs installing beyond Bootstrap itself.
You can build it yourself from an ordered list, spacing utilities and CSS for the line and dots. Or you can reach for a UI kit. MDB lists a timeline among its extended components, and Themesberg Pixel documents timeline markup that uses timeline and timeline-item classes. There’s a package route too, since MDB’s bootstrap-timeline 1.0.0 is available on npm, cdnjs and jsDelivr.
Everything here targets Bootstrap 5.3. The Bootstrap team’s 4.6.2 release post set Bootstrap 4 end of life for January 1, 2023, so there’s little reason to start a new timeline on it. Bootstrap 6 reached its first alpha, 6.0.0-alpha.1, on October 8, 2026, but it’s a pre-release that requires Chrome and Edge 130, Firefox 132 or Safari 18. For now 5.3 is the version to build on.
Which method fits: grid, list group, Flexbox or a package?
A list group or cards covers a plain vertical timeline. The grid gives you a centered zig-zag, Flexbox gives you a horizontal row, and a package only earns its place when you want ready-made styling.
Each method trades markup weight against custom CSS. The freefrontend roundup of 20+ Bootstrap timelines spans vertical, left-right and horizontal layouts. Its introduction points out that Bootstrap’s grid and positioning utilities can draw the axis while the content stays in a predictable document order.
| Method | Orientation | Custom CSS | Best for |
|---|
| List group or cards | Vertical | Line and dots only | Changelogs, resumes, short histories |
| Grid plus pseudo-elements | Alternating | Line, dots, side flip | Company history pages |
| Flexbox row | Horizontal | Line, dots, overflow | Process steps, roadmaps |
| Package (MDB bootstrap-timeline) | Vertical and horizontal | Prebuilt stylesheet | Fast demos, themed sites |
A list group or cards needs the least markup, because Bootstrap supplies the borders and spacing. What it doesn’t supply is a connector line, so the pseudo-element CSS is still yours to write.
The grid gives full control over which side each event lands on, through offset and order classes. It also costs the most custom CSS of the four, and the dot offsets have to match column edges.
Flexbox gets you equal-width steps with flex-fill and no plugin. It stacks into a column below a breakpoint with flex-column and flex-md-row. Long timelines overflow narrow screens, though.
With a package, horizontal, vertical, image and gradient variants come ready, and the cdnjs listing carries standard, dark, RTL and dark RTL stylesheets. You load and update one more stylesheet, and your class names follow MDB’s markup.
Packages are tempting. A list group timeline is short enough to own, and owning the CSS means nothing extra to update when Bootstrap moves on.
If the project doesn’t need the framework at all, a plain CSS timeline drops that weight too. And before copying any CodePen demo, check which Bootstrap version it loads.
How to build a vertical timeline in Bootstrap 5
Load Bootstrap 5.3 and wrap the events in an ordered list. Each item holds a time element for the date, a heading and a paragraph, and CSS draws the line and dots afterwards. The timeline needs the stylesheet only. Bootstrap’s JavaScript plays no part.
Base markup
- Load the Bootstrap 5.3 stylesheet from jsDelivr (cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css), using the link tag with its integrity attribute from the Bootstrap docs. Add your own stylesheet after it.
- Wrap the list in a container with vertical padding, for example container py-5.
- Give the ordered list the classes timeline, d-flex, flex-column and gap-5. The gap utility spaces the events, so no item carries a bottom margin that stretches the line past the last dot.
- Write each event as a list item holding a time element with a datetime value, a heading and a paragraph.
- Add the line and marker CSS from the next section.
<ol class="timeline d-flex flex-column gap-5">
<li class="timeline-item">
<time class="d-block fw-bold text-body-secondary" datetime="2024-03-11">11 March 2024</time>
<h3 class="h5 mb-1">Company founded</h3>
<p class="mb-0">Two people, one office, first prototype.</p>
</li>
<li class="timeline-item">
<time class="d-block fw-bold text-body-secondary" datetime="2024-09-02">2 September 2024</time>
<h3 class="h5 mb-1">First customer</h3>
<p class="mb-0">A pilot account goes live.</p>
</li>
</ol>
The time element has been Baseline widely available since April 24, 2020, according to WebDX web-features data, so the datetime attribute needs no fallback.
Older snippets color the date with text-muted. The 5.3 documentation uses text-body-secondary in its own list group examples.
Event content in cards
Swap the bare list item content for a card when an event carries more than a sentence. Bootstrap cards add a border, padding and rounded corners without extra CSS.
Put a div with the card class inside the li and a div with card-body inside that. The time element, a heading with card-title and a paragraph with card-text go in the body. If the date should sit on its own bar, use card-header instead. Either way, recalculate the dot’s top offset, because card-body padding pushes the first text line down.
How to draw the connector line and markers
Both are pseudo-elements. The line is a ::before on the list and each dot is a ::before on the list item, all absolutely positioned. Custom properties keep the offsets in sync when the gutter or dot size changes.
Line and dot with pseudo-elements
.timeline {
--tl-gutter: 2rem;
--tl-dot: 1rem;
--tl-line: 2px;
position: relative;
margin: 0;
padding-left: var(--tl-gutter);
list-style: none;
}
.timeline::before {
content: "";
position: absolute;
top: 0;
bottom: 0;
left: calc(var(--tl-dot) / 2 - var(--tl-line) / 2);
width: var(--tl-line);
background-color: var(--bs-border-color);
}
.timeline-item {
position: relative;
}
.timeline-item::before {
content: "";
position: absolute;
top: 0.25rem;
left: calc(var(--tl-gutter) * -1);
width: var(--tl-dot);
height: var(--tl-dot);
border-radius: 50%;
background-color: var(--bs-primary);
box-shadow: 0 0 0 4px var(--bs-body-bg);
}
Bootstrap uses the same technique internally. Its numbered list group generates numbers with counter-reset on the ol and a ::before pseudo-element on each li (Bootstrap list group docs, v5.3).
Hard-coded offsets are where this breaks. Change the padding or dot size without updating left, and the dot slides off the line. The calc() values above derive from three variables, so one edit moves everything.
Icon markers
Replace the dot with an icon when events fall into types. Bootstrap Icons are SVG-based, so they work as inline SVG, img elements, an SVG sprite or the icon font.
Place the icon in a span with position-absolute and the same left offset as the dot, and hide the pseudo-element dot on those items or the marker doubles up. Font Awesome works the same way (the freefrontend accordion timeline lists it as a requirement next to Bootstrap).
Alternating and horizontal timeline layouts
From the md breakpoint up, an alternating timeline centers the line and flips events between left and right. The horizontal kind is simpler to describe: a Flexbox row with the line running along the top.
Alternating timeline with the grid
Each li carries the row class, so the list stays an ordered list. Left events take col-md-6 with text-md-end, and right events take col-md-6 with offset-md-6. The line is one pseudo-element at left: 50% on the wrapper. Dots sit on the column edge that faces the line, which means position-relative on the column.
Bootstrap’s twelve-column grid system handles the side flip. The order-md classes swap columns when a spacer column sits between the content and the line.
MDB’s own basic example skips the grid. It sets each event container to 50% width, pushes right-side containers with left: 50%, and draws a 6px line at left: 50% (MDB bootstrap-timeline repository).
Both routes produce the same zig-zag. The grid route keeps spacing and breakpoints in Bootstrap classes, which is why it’s the better fit for a Bootstrap project.
Either route needs a side class on every item unless you flip with :nth-child(even). Use :nth-child when events get reordered often.
Horizontal timeline with Flexbox
Flexbox does the layout. The ol gets d-flex, each li gets flex-fill, and the line is a border-top on each item with a dot pseudo-element sitting on it.
Bootstrap’s list group docs describe the same pattern. The flex-fill class makes horizontal items equal width, and list-group-horizontal-md starts the row at the md breakpoint.
The markup doesn’t change from the vertical version, still an ordered list with a time element, a heading and a paragraph. The CSS does: line direction, dot position and item width. Wrap the row in overflow-auto when it can exceed the screen width.
Horizontal layouts fit short labels. Paragraph-length events belong in the vertical layouts.
How the timeline collapses across breakpoints
Build the timeline as one column with the line on the left, then switch to the alternating layout at the md breakpoint, which starts at 768px.
Bootstrap’s own CSS works mobile first, and the timeline should too. One min-width query then carries the whole layout change.
The Bootstrap 5.3 breakpoints documentation (v5.3.8) gives these values:
- sm starts at 576px
- md starts at 768px, the usual switch point for a two-sided timeline
- lg starts at 992px
- xl and xxl start at 1200px and 1400px
- Max-width queries for md and below end at 767.98px (the breakpoint minus .02px)
The CSS below draws the line on the left edge and moves it to the center at 768px. Add position-relative to each column and g-0 to each row, so the two columns meet exactly at the line.
.timeline-alt {
--tl-gutter: 2rem;
--tl-dot: 1rem;
--tl-line: 2px;
position: relative;
margin: 0;
padding-left: var(--tl-gutter);
list-style: none;
}
.timeline-alt::before {
content: "";
position: absolute;
top: 0;
bottom: 0;
left: calc(var(--tl-dot) / 2 - var(--tl-line) / 2);
width: var(--tl-line);
background-color: var(--bs-border-color);
}
.timeline-alt .col-md-6::before {
content: "";
position: absolute;
top: 0.25rem;
left: calc(var(--tl-gutter) * -1);
width: var(--tl-dot);
height: var(--tl-dot);
border-radius: 50%;
background-color: var(--bs-primary);
box-shadow: 0 0 0 4px var(--bs-body-bg);
}
@media (min-width: 768px) {
.timeline-alt {
padding-left: 0;
}
.timeline-alt::before {
left: 50%;
transform: translateX(-50%);
}
.timeline-alt .col-md-6::before {
left: auto;
right: calc(var(--tl-dot) / -2);
}
.timeline-alt .col-md-6.offset-md-6::before {
left: calc(var(--tl-dot) / -2);
right: auto;
}
}
The 768px stays a literal value. Bootstrap’s docs state that CSS variables don’t work in media queries, even though the breakpoints exist as variables.
In Sass, the media-breakpoint-up(md) mixin writes the same query from the breakpoint map.
Copy-paste timelines pick their own collapse width, and the sources don’t agree. The bootstrap-timeline repository switches layouts at max-width: 600px, a value that matches no Bootstrap breakpoint. Combined with col-md columns, that rule leaves a band from 601px to 767px where the line is centered but the columns are still stacked full width. Use Bootstrap’s breakpoints for every query in the timeline.
The horizontal version follows the same idea. Below md it uses flex-column, with the line on each item’s left edge. From md up it switches to flex-md-row and moves the line to each item’s top edge, inside the same min-width query.
Stacking beats horizontal scrolling on narrow screens, because events hidden past the edge go unread.
Customizing colors, line width and spacing
Three custom properties on the list set line width, dot size and gutter. Bootstrap’s own –bs variables handle the colors, and a Sass override changes the theme before compiling.
| Target | Tool | Where |
|---|
| Line width, dot size, gutter | –tl-line, –tl-dot, –tl-gutter | On the list, or on a parent to theme several timelines |
| Line and dot ring colors | –bs-border-color, –bs-body-bg | Already read by the CSS |
| Dot color | –bs-primary or a per-item property | The dot rule |
| Theme-wide accent | $primary in Sass | custom.scss, before variables are imported |
| Event spacing | gap-3 to gap-5 | On the ol |
Dark mode needs no extra CSS. The timeline reads –bs-body-bg and –bs-border-color, and Bootstrap 5.3 redefines both under data-bs-theme=”dark”.
To color one dot differently, change the dot’s background-color to var(–tl-dot-color, var(–bs-primary)). Then style=”–tl-dot-color: var(–bs-success)” on a single li recolors that dot only.
For Sass, import functions first and set overrides such as $primary next. After that come variables, variables-dark, maps, mixins and root. Every Bootstrap variable carries the !default flag, so a value assigned earlier wins (Bootstrap Sass documentation).
For one or two changes, skip Sass and override Bootstrap’s default styles in your own stylesheet. Sass pays off once the colors multiply across components.
Making a Bootstrap timeline accessible
Use an ordered list for the sequence and keep the DOM order chronological. Hide purely decorative markers from assistive technology, then confirm Safari still announces the list.
The ol and li tell screen readers how many events exist and in what order. Bootstrap’s order utilities change the visual order only, since screen readers follow the markup. Inline SVG or icon-font markers get aria-hidden=”true” when the event text already says what happened. And while Bootstrap disables most of its own transitions under prefers-reduced-motion, any scroll animation you add needs the same guard.
The timeline CSS sets list-style: none, which makes Safari drop the list role for VoiceOver.
Sources disagree on the scope. A December 2025 WebKit Bugzilla report (bug 304137) says iOS VoiceOver reads such lists as plain text while macOS VoiceOver still announces them. WebKit closed it in February 2026 as a duplicate of its list-detection heuristics bug, so the behavior is intended.
Older write-ups describe both platforms as affected, including Scott O’Hara’s 2019 post on lists and Safari. Cssence’s 2022 test found the behavior still present in Safari 15.
Adding role=”list” to the ol, a small ARIA fix, covers either case. Validators flag it as redundant, which is the accepted trade-off.
WCAG 2.2 requires 4.5:1 for normal text and 3:1 for non-text elements that carry meaning (W3C), and Bootstrap’s accessibility documentation says parts of its default palette fall below both on light backgrounds.
Test the date text and any colored dots for color contrast. The plain line and dots count as decoration, while dots that encode event type need 3:1 plus a text label, which the visually-hidden helper provides.
When a Bootstrap timeline breaks down
A Bootstrap timeline breaks when it depends on another Bootstrap version, a script plugin, physical left and right values, or a list far longer than a page of events.
Every row below comes from the Bootstrap 5 migration guide, except the plugin row.
| Failure | What breaks | Fix |
|---|
| Snippet from Bootstrap 3 or 4 | ml- and mr- classes, text-left, float-left and data-toggle stop working | Use ms-/me-, text-start, float-start, data-bs-toggle |
| Dots anchored to columns | Columns lost position: relative in v5, so dots attach to a farther ancestor | Add position-relative to the column |
| jQuery-based horizontal plugin | Slick-style sliders need jQuery, which Bootstrap 5 dropped as a dependency | Rebuild the row with Flexbox |
| Physical left and right in RTL | Bootstrap 5 RTL uses logical start and end, so custom left and right do not flip | Use inset-inline-start and inset-inline-end |
| Deprecated utilities | text-muted is deprecated since v5.3.0, removal planned for v6.0.0 | Switch to text-body-secondary |
Gallery roundups still list timelines built on Bootstrap 3.3.6 and on Bootstrap 4.5.2 with Slick 1.9.0 (Slider Revolution’s collection). Check the Bootstrap link tag before copying any of them.
Bootstrap 5 dropped Internet Explorer 10 and 11, and custom properties don’t run in Internet Explorer either, so cross-browser compatibility testing stops at current Chrome, Firefox, Safari and Edge.
The last limit is volume. A timeline of hundreds of events is a data view, and a plain CSS list was never built for it.
Bootstrap timeline FAQ
Is JavaScript required for a Bootstrap timeline?
No. The static version runs on CSS alone.
JavaScript only comes in for extras, such as the Collapse plugin for expandable event details, which needs Bootstrap’s JavaScript.
Can a timeline animate as the user scrolls?
Yes, with a few lines of your own code. Bootstrap ships no scroll-reveal utility, and its Scrollspy plugin (rewritten on the Intersection Observer API in v5.2.0) only highlights nav and list group items.
Observe each timeline item yourself and add a class that triggers a CSS transition.
Which content suits a timeline better than a list or table?
Anything where order carries meaning, like a company history, a project roadmap, a resume or the tracking steps of an order.
Items with no sequence read faster in a list group or table, because the line and dates add weight without adding information.
Can the timeline run in React, Bootstrap Studio or WordPress?
Yes, in all three. React takes the same markup with class changed to className, and Bootstrap Studio accepts a pasted HTML snippet in its HTML element, which is how Common Ninja’s timeline widget gets added.
Web-Boots, an HTML theme built on Bootstrap 4 (not a WordPress theme), ships a timeline block, and WordPress has plugins such as Timeline Block that add timelines to the Gutenberg editor. On a theme without Bootstrap, add Bootstrap to WordPress before pasting custom markup.
When a Bootstrap timeline outgrows plain CSS
It outgrows plain CSS when the events come from data or the list runs past one screen. Scroll animation pushes it there too.
Add scripting in a fixed order. Generate the list items from data first, collapse older events behind a show-more control next, and add scroll animation last. Each step keeps the static markup usable if the next one fails.
The trade-off is a JavaScript dependency. Generated markup vanishes when scripts fail, unless the server renders the first batch of events in HTML.
The advice was verified on October 29, 2026, when the documentation’s version list showed 5.3.x as the latest release (last update v5.3.8) and Bootstrap 6 only as an alpha, 6.0.0-alpha.1. A stable major version would likely break copied class names, as the move from 4 to 5 did.
Scroll animation is the step with its own learning curve, and CSS animations on scroll is the place to start.