Bootstrap Session Activity Timeline — Free HTML CSS JS Snippet
Bootstrap Session Activity Timeline · Dashboards · Plain HTML, CSS & JS · Live preview
Copy & paste
Source Code
<div class="container py-5 d-flex justify-content-center">
<div class="card bssat-card">
<div class="card-body p-3">
<h6 class="fw-bold mb-2">Active sessions</h6>
<ul class="list-unstyled mb-0" id="bssatList"></ul>
</div>
</div>
</div>.bssat-card { width: 400px; max-width: 100%; border: 1px solid #eceef1; border-radius: 14px; }
.bssat-row { display: flex; gap: 10px; padding: 10px 0; border-bottom: 1px solid #f1f2f5; }
.bssat-row:last-child { border-bottom: none; }
.bssat-icon {
width: 32px; height: 32px; border-radius: 8px; background: #eceef3;
display: flex; align-items: center; justify-content: center; flex-shrink: 0; font-size: 15px;
}
.bssat-meta { font: 700 10.5px ui-monospace, monospace; color: #9ca3af; }
.bssat-signout { border: none; background: none; color: #dc3545; font: 700 11.5px system-ui, sans-serif; cursor: pointer; }const SESSIONS = [
{ device: 'MacBook Pro', icon: '\u{1F4BB}', location: 'San Francisco, US', time: 'Active now', current: true },
{ device: 'iPhone 15', icon: '\u{1F4F1}', location: 'San Francisco, US', time: '2 hours ago', current: false },
{ device: 'Windows PC', icon: '\u{1F5A5}\uFE0F', location: 'Austin, US', time: 'Yesterday', current: false },
{ device: 'iPad Air', icon: '\u{1F4F1}', location: 'Chicago, US', time: '4 days ago', current: false },
];
const list = document.getElementById('bssatList');
let sessions = SESSIONS.map((s, id) => ({ ...s, id }));
function render() {
list.innerHTML = sessions.map(s =>
'<li class="bssat-row" data-id="' + s.id + '">' +
'<div class="bssat-icon">' + s.icon + '</div>' +
'<div class="flex-grow-1">' +
'<div class="d-flex justify-content-between">' +
'<strong class="small">' + s.device + (s.current ? ' <span class="badge text-bg-success ms-1">This device</span>' : '') + '</strong>' +
'</div>' +
'<div class="bssat-meta">' + s.location + ' · ' + s.time + '</div>' +
'</div>' +
(s.current ? '' : '<button type="button" class="bssat-signout" data-id="' + s.id + '">Sign out</button>') +
'</li>'
).join('');
}
list.addEventListener('click', e => {
const btn = e.target.closest('.bssat-signout');
if (!btn) return;
sessions = sessions.filter(s => s.id !== Number(btn.dataset.id));
render();
});
render();Extend with AI
Take This Snippet Further With AI
The AI prompt for this one is for Patreon supporters ($3 a month, 7 days free), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.
What's included
Features
About this UI Snippet
Bootstrap Session Activity Timeline — HTML, CSS & JavaScript

This is a security-data-specific pattern, distinct from a decorative timeline component like bootstrap-timeline-vertical — every row here carries real account-security fields (device, location, last-active time) and a genuine action (revoking a session), not just a chronological list of generic events.
The current: true flag on one session is what drives two related pieces of behavior at once: it adds the green "This device" badge, and it's checked inline (s.current ? '' : '...Sign out button...') to withhold the Sign out button entirely from the current session — a real account-security page shouldn't let a user casually sign out of the very device they're using to view the list, since doing so from inside the app they're currently using would immediately invalidate the session rendering the button they just clicked.
Signing out of another session calls sessions.filter(s => s.id !== ...) and re-renders — in this demo that's purely a local list update, but the shape of it (remove by id, re-render from the updated array) is exactly what wiring up a real revoke-session API call would look like, with the actual network request added right alongside the filter rather than replacing it.
Step by step
How to Use
- 1Load the snippetFour sessions list, the first (MacBook Pro) marked "This device" with no Sign out button.
- 2Look at the current device's rowIt has no Sign out action — only other sessions can be revoked from this view.
- 3Click "Sign out" on the iPhone sessionThat row disappears from the list immediately.
- 4Sign out of the remaining other sessionsOnly the current device's session remains, with no Sign out button to click at all.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
Because the list is being viewed from that exact session — revoking it would invalidate the page rendering the button mid-click. Signing out of the current device belongs in a dedicated, explicit "Log out" action elsewhere, not mixed into a list meant for reviewing other sessions.
This demo only removes the row from a local array; a real implementation must call an actual backend endpoint to revoke that session's token, and should only remove the row from the UI once that request succeeds.
Yes. Keep the sessions array in component state, mark the current session with the same current flag, and derive the Sign out button's visibility per row from that flag in your render function.
Add a button that filters sessions down to just the entry with current: true, calling your real revoke-session API for every other session id first — the same underlying pattern as the per-row action, applied to the whole list at once.