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

At minimum: a sidebar with navigation, a top header bar, stat cards showing key metrics, a data table for managing records, and responsive layout that works on tablets. Charts are optional but common. Bootstrap 5 provides the grid, components and utilities — you wire them together.
Use position:sticky; top:0; height:100vh; overflow-y:auto on the sidebar. This keeps it fixed while the main content scrolls. Works without JavaScript. Alternatively use position:fixed and add margin-left equal to sidebar width on the main content.
Use Chart.js — it's lightweight and works well with Bootstrap. Add the CDN link, create a canvas element inside a Bootstrap card, then initialize the chart in JavaScript: new Chart(canvas, { type: 'line', data: {...} }). Chart.js 4 is the current version.
Yes. Bootstrap 5 is one of the best choices for admin dashboards because it ships with all the components you need — tables, forms, badges, modals, dropdowns — and its grid handles the sidebar + content layout cleanly. The main thing you add is custom sidebar CSS and chart integration.
Hide the sidebar on mobile using d-none d-lg-block. Add an off-canvas sidebar for mobile triggered by a hamburger button. Stack stat cards using col-sm-6 col-xl-3 so they go 1→2→4 columns across breakpoints. Tables use table-responsive wrapper for horizontal scrolling on small screens.

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.

Related Components