I've built a lot of admin dashboards with Bootstrap 5. It's genuinely one of the best CSS frameworks for this use case — every component you need ships out of the box. Here's how to build one from scratch, step by step.
What We're Building
A complete admin dashboard with:
- Fixed sidebar navigation with active states
- Top header bar with search and user menu
- Stats cards row with metric numbers
- Line chart using Chart.js
- Recent orders data table
- Responsive layout — collapses to off-canvas sidebar on mobile
Project Setup
Create index.html and add these in <head>:
<!-- Bootstrap 5 CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Bootstrap Icons -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" rel="stylesheet">
<!-- Chart.js -->
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
Before closing </body>:
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
Step 1 — Dashboard Layout Shell
The outer layout is a flex container with the sidebar on the left and main content on the right:
<body style="background:#f8f9fa;">
<!-- Mobile off-canvas sidebar -->
<div class="offcanvas offcanvas-start" tabindex="-1" id="mobileSidebar" style="width:240px;">
<div class="offcanvas-header border-bottom">
<span class="fw-bold" style="color:#fd4766;">AdminPanel</span>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas"></button>
</div>
<div class="offcanvas-body p-0" id="mobileSidebarNav">
<!-- Nav items injected by JS or duplicated here -->
</div>
</div>
<div class="d-flex">
<!-- ─── SIDEBAR (desktop) ─── -->
<aside id="sidebar" class="d-none d-lg-flex flex-column bg-white border-end"
style="width:240px;min-height:100vh;position:sticky;top:0;height:100vh;overflow-y:auto;flex-shrink:0;">
<!-- Brand -->
<div class="px-4 py-3 border-bottom d-flex align-items-center gap-2">
<div class="rounded d-flex align-items-center justify-content-center text-white fw-bold"
style="width:28px;height:28px;background:#fd4766;font-size:0.75rem;flex-shrink:0;">AP</div>
<span class="fw-bold" style="color:#fd4766;">AdminPanel</span>
</div>
<!-- Nav -->
<nav class="flex-grow-1 py-2">
<div class="px-3 mb-1" style="font-size:0.65rem;color:#9ca3af;text-transform:uppercase;letter-spacing:0.08em;padding-top:8px;">
Main
</div>
<a href="#" class="d-flex align-items-center gap-2 px-3 py-2 mx-2 rounded text-decoration-none fw-semibold sidebar-link active"
style="color:#fd4766;background:rgba(253,71,102,0.08);">
<i class="bi bi-grid" style="width:18px;"></i>
<span style="font-size:0.88rem;">Dashboard</span>
</a>
<a href="#" class="d-flex align-items-center gap-2 px-3 py-2 mx-2 rounded text-decoration-none sidebar-link" style="color:#6b7280;">
<i class="bi bi-box" style="width:18px;"></i>
<span style="font-size:0.88rem;">Products</span>
<span class="badge ms-auto" style="background:#fd4766;font-size:0.6rem;">12</span>
</a>
<a href="#" class="d-flex align-items-center gap-2 px-3 py-2 mx-2 rounded text-decoration-none sidebar-link" style="color:#6b7280;">
<i class="bi bi-receipt" style="width:18px;"></i>
<span style="font-size:0.88rem;">Orders</span>
</a>
<a href="#" class="d-flex align-items-center gap-2 px-3 py-2 mx-2 rounded text-decoration-none sidebar-link" style="color:#6b7280;">
<i class="bi bi-people" style="width:18px;"></i>
<span style="font-size:0.88rem;">Customers</span>
</a>
<a href="#" class="d-flex align-items-center gap-2 px-3 py-2 mx-2 rounded text-decoration-none sidebar-link" style="color:#6b7280;">
<i class="bi bi-bar-chart" style="width:18px;"></i>
<span style="font-size:0.88rem;">Analytics</span>
</a>
<div class="px-3 mb-1 mt-3" style="font-size:0.65rem;color:#9ca3af;text-transform:uppercase;letter-spacing:0.08em;">
Settings
</div>
<a href="#" class="d-flex align-items-center gap-2 px-3 py-2 mx-2 rounded text-decoration-none sidebar-link" style="color:#6b7280;">
<i class="bi bi-gear" style="width:18px;"></i>
<span style="font-size:0.88rem;">Settings</span>
</a>
</nav>
<!-- User footer -->
<div class="px-3 py-3 border-top d-flex align-items-center gap-2">
<div class="rounded-circle text-white d-flex align-items-center justify-content-center fw-bold flex-shrink-0"
style="width:32px;height:32px;background:#fd4766;font-size:0.72rem;">GS</div>
<div style="min-width:0;">
<div class="fw-semibold" style="font-size:0.82rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">Gagan Singh</div>
<div class="text-muted" style="font-size:0.7rem;">Founder</div>
</div>
</div>
</aside>
<!-- ─── MAIN ─── -->
<div class="flex-grow-1" style="min-width:0;">
<!-- Content goes in Step 2 and 3 -->
</div>
</div>
</body>
Add this CSS for sidebar link hover:
<style>
.sidebar-link:hover { background: #f3f4f6; color: #111 !important; }
</style>
Step 2 — Top Header Bar
Inside the main <div class="flex-grow-1"> add the header first:
<!-- Header -->
<header class="bg-white border-bottom sticky-top px-4 py-2 d-flex align-items-center gap-3">
<!-- Mobile hamburger -->
<button class="btn btn-sm btn-outline-secondary d-lg-none border-0"
data-bs-toggle="offcanvas" data-bs-target="#mobileSidebar">
<i class="bi bi-list fs-5"></i>
</button>
<!-- Page title -->
<h6 class="fw-bold mb-0 me-auto" style="font-size:0.95rem;">Dashboard Overview</h6>
<!-- Search -->
<div class="d-none d-md-flex align-items-center gap-2 border rounded px-3 py-1"
style="background:#f8f9fa;min-width:200px;">
<i class="bi bi-search text-muted" style="font-size:0.8rem;"></i>
<input type="search" class="border-0 bg-transparent outline-none"
placeholder="Search..." style="outline:none;font-size:0.85rem;width:100%;">
</div>
<!-- Notifications -->
<button class="btn btn-sm btn-outline-secondary border-0 position-relative">
<i class="bi bi-bell"></i>
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger"
style="font-size:0.6rem;">4</span>
</button>
<!-- User -->
<div class="dropdown">
<button class="btn btn-sm btn-light d-flex align-items-center gap-2 border" data-bs-toggle="dropdown">
<div class="rounded-circle text-white d-flex align-items-center justify-content-center fw-bold"
style="width:24px;height:24px;background:#fd4766;font-size:0.62rem;flex-shrink:0;">GS</div>
<span class="d-none d-sm-inline" style="font-size:0.82rem;">Gagan</span>
<i class="bi bi-chevron-down" style="font-size:0.65rem;"></i>
</button>
<ul class="dropdown-menu dropdown-menu-end shadow border-0" style="font-size:0.85rem;">
<li><a class="dropdown-item d-flex align-items-center gap-2" href="#"><i class="bi bi-person text-muted"></i> Profile</a></li>
<li><a class="dropdown-item d-flex align-items-center gap-2" href="#"><i class="bi bi-gear text-muted"></i> Settings</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item d-flex align-items-center gap-2 text-danger" href="#"><i class="bi bi-box-arrow-right"></i> Sign Out</a></li>
</ul>
</div>
</header>
Step 3 — Stats Cards
Below the header, add the main content area:
<!-- Main content -->
<main class="p-4">
<!-- Stats row -->
<div class="row g-3 mb-4">
<div class="col-sm-6 col-xl-3">
<div class="card border-0 shadow-sm">
<div class="card-body p-3">
<div class="d-flex align-items-start justify-content-between mb-2">
<p class="text-muted small mb-0">Total Revenue</p>
<div class="rounded-2 d-flex align-items-center justify-content-center"
style="width:36px;height:36px;background:rgba(253,71,102,0.1);">
<i class="bi bi-currency-dollar" style="color:#fd4766;"></i>
</div>
</div>
<h4 class="fw-bold mb-1">$12,480</h4>
<p class="mb-0 small"><span class="text-success fw-semibold">↑ 8.2%</span> <span class="text-muted">vs last month</span></p>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-3">
<div class="card border-0 shadow-sm">
<div class="card-body p-3">
<div class="d-flex align-items-start justify-content-between mb-2">
<p class="text-muted small mb-0">Total Orders</p>
<div class="rounded-2 d-flex align-items-center justify-content-center"
style="width:36px;height:36px;background:rgba(79,110,247,0.1);">
<i class="bi bi-bag" style="color:#4f6ef7;"></i>
</div>
</div>
<h4 class="fw-bold mb-1">1,240</h4>
<p class="mb-0 small"><span class="text-success fw-semibold">↑ 12.5%</span> <span class="text-muted">vs last month</span></p>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-3">
<div class="card border-0 shadow-sm">
<div class="card-body p-3">
<div class="d-flex align-items-start justify-content-between mb-2">
<p class="text-muted small mb-0">Active Users</p>
<div class="rounded-2 d-flex align-items-center justify-content-center"
style="width:36px;height:36px;background:rgba(16,185,129,0.1);">
<i class="bi bi-people" style="color:#10b981;"></i>
</div>
</div>
<h4 class="fw-bold mb-1">3,847</h4>
<p class="mb-0 small"><span class="text-danger fw-semibold">↓ 2.4%</span> <span class="text-muted">vs last month</span></p>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-3">
<div class="card border-0 shadow-sm">
<div class="card-body p-3">
<div class="d-flex align-items-start justify-content-between mb-2">
<p class="text-muted small mb-0">Avg. Rating</p>
<div class="rounded-2 d-flex align-items-center justify-content-center"
style="width:36px;height:36px;background:rgba(245,158,11,0.1);">
<i class="bi bi-star" style="color:#f59e0b;"></i>
</div>
</div>
<h4 class="fw-bold mb-1">4.9 ⭐</h4>
<p class="mb-0 small"><span class="text-success fw-semibold">↑ 0.1</span> <span class="text-muted">vs last month</span></p>
</div>
</div>
</div>
</div>
Step 4 — Revenue Chart
Add a Chart.js line chart below the stats cards:
<!-- Chart row -->
<div class="row g-3 mb-4">
<div class="col-lg-8">
<div class="card border-0 shadow-sm">
<div class="card-header bg-white border-0 d-flex align-items-center justify-content-between pt-3 pb-0 px-4">
<h6 class="fw-bold mb-0">Revenue Overview</h6>
<select class="form-select form-select-sm border-0 bg-light" style="width:auto;font-size:0.8rem;">
<option>Last 6 months</option>
<option>Last year</option>
</select>
</div>
<div class="card-body px-4 pb-4 pt-3">
<canvas id="revenueChart" height="120"></canvas>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="card border-0 shadow-sm h-100">
<div class="card-header bg-white border-0 pt-3 px-4 pb-0">
<h6 class="fw-bold mb-0">Top Templates</h6>
</div>
<div class="card-body px-4">
<div class="d-flex flex-column gap-3">
<div>
<div class="d-flex justify-content-between mb-1">
<span class="small fw-semibold">Marvel Dashboard</span>
<span class="small text-muted">62%</span>
</div>
<div class="progress" style="height:6px;">
<div class="progress-bar" style="width:62%;background:#fd4766;"></div>
</div>
</div>
<div>
<div class="d-flex justify-content-between mb-1">
<span class="small fw-semibold">PORTO Template</span>
<span class="small text-muted">24%</span>
</div>
<div class="progress" style="height:6px;">
<div class="progress-bar" style="width:24%;background:#4f6ef7;"></div>
</div>
</div>
<div>
<div class="d-flex justify-content-between mb-1">
<span class="small fw-semibold">Proctu Medical</span>
<span class="small text-muted">14%</span>
</div>
<div class="progress" style="height:6px;">
<div class="progress-bar" style="width:14%;background:#10b981;"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
Initialize the chart in a <script> tag at the bottom:
const ctx = document.getElementById('revenueChart').getContext('2d')
new Chart(ctx, {
type: 'line',
data: {
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
datasets: [{
label: 'Revenue',
data: [1800, 2200, 1950, 2800, 2400, 3100],
borderColor: '#fd4766',
backgroundColor: 'rgba(253,71,102,0.08)',
borderWidth: 2,
fill: true,
tension: 0.4,
pointBackgroundColor: '#fd4766',
pointRadius: 4,
}]
},
options: {
responsive: true,
plugins: { legend: { display: false } },
scales: {
y: {
beginAtZero: false,
grid: { color: '#f3f4f6' },
ticks: { callback: (v) => '$' + v }
},
x: { grid: { display: false } }
}
}
})
Step 5 — Recent Orders Table
<!-- Orders table -->
<div class="card border-0 shadow-sm mb-4">
<div class="card-header bg-white border-0 d-flex align-items-center justify-content-between pt-3 pb-0 px-4">
<h6 class="fw-bold mb-0">Recent Orders</h6>
<a href="#" class="btn btn-sm text-white px-3" style="background:#fd4766;font-size:0.78rem;">View All</a>
</div>
<div class="card-body px-0 pb-0">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead>
<tr class="border-top" style="background:#f9fafb;">
<th class="px-4 py-3 small fw-semibold text-muted border-0">Order ID</th>
<th class="py-3 small fw-semibold text-muted border-0">Template</th>
<th class="py-3 small fw-semibold text-muted border-0">Customer</th>
<th class="py-3 small fw-semibold text-muted border-0">Date</th>
<th class="py-3 small fw-semibold text-muted border-0">Amount</th>
<th class="py-3 small fw-semibold text-muted border-0">Status</th>
</tr>
</thead>
<tbody>
<tr>
<td class="px-4 py-3 small text-muted">#ORD-001</td>
<td class="py-3 small fw-semibold">Marvel Dashboard</td>
<td class="py-3 small text-muted">Rahul Kumar</td>
<td class="py-3 small text-muted">Jun 10, 2026</td>
<td class="py-3 small fw-bold" style="color:#fd4766;">$29</td>
<td class="py-3"><span class="badge bg-success bg-opacity-10 text-success" style="font-size:0.72rem;">Completed</span></td>
</tr>
<tr>
<td class="px-4 py-3 small text-muted">#ORD-002</td>
<td class="py-3 small fw-semibold">PORTO Template</td>
<td class="py-3 small text-muted">Priya Sharma</td>
<td class="py-3 small text-muted">Jun 09, 2026</td>
<td class="py-3 small fw-bold" style="color:#fd4766;">$19</td>
<td class="py-3"><span class="badge bg-success bg-opacity-10 text-success" style="font-size:0.72rem;">Completed</span></td>
</tr>
<tr>
<td class="px-4 py-3 small text-muted">#ORD-003</td>
<td class="py-3 small fw-semibold">Proctu Medical</td>
<td class="py-3 small text-muted">Amit Verma</td>
<td class="py-3 small text-muted">Jun 09, 2026</td>
<td class="py-3 small fw-bold" style="color:#fd4766;">$39</td>
<td class="py-3"><span class="badge bg-warning bg-opacity-10 text-warning" style="font-size:0.72rem;">Pending</span></td>
</tr>
<tr>
<td class="px-4 py-3 small text-muted">#ORD-004</td>
<td class="py-3 small fw-semibold">Marvel Dashboard</td>
<td class="py-3 small text-muted">Sneha Patel</td>
<td class="py-3 small text-muted">Jun 08, 2026</td>
<td class="py-3 small fw-bold" style="color:#fd4766;">$29</td>
<td class="py-3"><span class="badge bg-danger bg-opacity-10 text-danger" style="font-size:0.72rem;">Refunded</span></td>
</tr>
<tr>
<td class="px-4 py-3 small text-muted">#ORD-005</td>
<td class="py-3 small fw-semibold">Kiosk Dashboard</td>
<td class="py-3 small text-muted">Dev Sharma</td>
<td class="py-3 small text-muted">Jun 08, 2026</td>
<td class="py-3 small fw-bold" style="color:#fd4766;">$29</td>
<td class="py-3"><span class="badge bg-success bg-opacity-10 text-success" style="font-size:0.72rem;">Completed</span></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</main>
Common Issues
Sidebar not sticky — Make sure position:sticky, top:0 and height:100vh are all on the sidebar. If the parent has overflow:hidden, sticky won't work — remove it.
Chart not rendering — Chart.js must be loaded before your script. Put the Chart.js CDN before your <script> tag.
Stats cards not equal height — Add h-100 to the card inside the column. The row is already flexbox so cards in the same row will match height automatically.
Table overflowing on mobile — Wrap the <table> in <div class="table-responsive">. This adds horizontal scroll instead of breaking the layout.
What to Add Next
This covers the core dashboard. Common additions:
- Dark mode — Add
data-bs-theme="dark"toggle to the header. Everything switches automatically with Bootstrap 5.3 - Collapsible sidebar — Track a boolean, toggle sidebar width between 240px and 64px with CSS transition
- More chart types — Doughnut chart for category breakdown, bar chart for monthly comparison
- Modal for adding records — Bootstrap modal with a form for creating new orders
- Pagination on the table — Bootstrap pagination component below the table
If you want a production-ready version of this with Angular 21, check out the Marvel Angular Dashboard — it's built on exactly this architecture. Use code FIRST30 for 30% off.
Frequently Asked Questions
Need a Full Bootstrap 5 Admin Dashboard?
Get a complete Angular 21 + Bootstrap 5 dashboard with 50+ components — built by the same team behind BootstrapPlanet.
Browse Templates →Use code FIRST30 for 30% off your first purchase.