Layout Blueprint

Every AdminLTE page follows the same structural blueprint: a wrapper that contains a header, a sidebar, a main content area, and (optionally) a footer. Understanding the four region classes is the difference between fighting the framework and using it productively.

The four regions
┌──────────────────────────────────────────┐
│  .app-wrapper                            │
│  ┌────────────────────────────────────┐  │
│  │  .app-header                       │  │ ← topbar / navbar
│  ├──────────┬─────────────────────────┤  │
│  │          │                         │  │
│  │  .app-   │   .app-main             │  │ ← page content
│  │ sidebar  │                         │  │
│  │          │                         │  │
│  ├──────────┴─────────────────────────┤  │
│  │  .app-footer (optional)            │  │
│  └────────────────────────────────────┘  │
└──────────────────────────────────────────┘

The wrapper is a CSS grid container, the four children are grid areas. You don’t have to know that to use it — the markup pattern alone is sufficient.

Minimal markup
<body class="layout-fixed sidebar-expand-lg bg-body-tertiary">
  <div class="app-wrapper">
    <nav class="app-header navbar navbar-expand bg-body"></nav>
    <aside class="app-sidebar bg-body-secondary shadow" data-bs-theme="dark"></aside>
    <main class="app-main"></main>
    <footer class="app-footer"></footer>
  </div>
</body>

Order inside .app-wrapper doesn’t matter — the grid template positions each child by its class. Use whatever order reads cleanly.

Region reference
.app-wrapper

The root container. Defines the grid template that positions header, sidebar, main, and footer. Always lives directly inside <body>.

You can add .compact-mode here (or anywhere up the tree) to reduce padding throughout the layout — useful for data-dense dashboards.

.app-header

The top bar. Use Bootstrap’s navbar classes (navbar navbar-expand bg-body) for the visual. The header is where the sidebar toggle button lives — wire it with data-lte-toggle="sidebar":

<a class="nav-link" data-lte-toggle="sidebar" href="#" role="button">
  <i class="bi bi-list"></i>
</a>

By default the header scrolls with the page. Add .fixed-header to <body> to pin it.

.app-sidebar

The side rail. Always contains two children: .sidebar-brand (logo / app name) and .sidebar-wrapper (the scrollable nav region).

<aside class="app-sidebar bg-body-secondary shadow" data-bs-theme="dark">
  <div class="sidebar-brand">
    <a href="/" class="brand-link">
      <span class="brand-text fw-light">My Dashboard</span>
    </a>
  </div>
  <div class="sidebar-wrapper">
    <nav class="mt-2">
      <ul class="nav sidebar-menu flex-column" data-lte-toggle="treeview" role="menu">
        <!-- nav items -->
      </ul>
    </nav>
  </div>
</aside>

The data-bs-theme="dark" on the sidebar gives you the dark-sidebar-on-light-page look from the demos. Drop it for a light sidebar.

.app-main

The page content region. Almost always contains two children:

<main class="app-main">
  <div class="app-content-header">
    <!-- page title + breadcrumb -->
  </div>
  <div class="app-content">
    <!-- page body, cards, tables, charts, etc. -->
  </div>
</main>

.app-content-header is the title strip — typically an <h3> and a breadcrumb in a row. .app-content is everything below it. Both should wrap their children in .container-fluid (or .container for capped width).

A footer strip at the bottom of the wrapper:

<footer class="app-footer">
  <div class="float-end d-none d-sm-inline">Version 4.0.0</div>
  <strong>&copy; 2026 Your Company</strong>
</footer>

Add .fixed-footer to <body> to pin it to the bottom of the viewport.

Body-level layout modifiers

Layout behaviour is mostly controlled by classes on <body>, not .app-wrapper. The supported modifiers:

Class What it does
layout-fixed Sidebar gets its own scrollbar; only .app-main scrolls with the page
fixed-header Sticky .app-header at the top; sidebar also pins (since v4.0.0)
fixed-footer Sticky .app-footer at the bottom
sidebar-expand-{sm,md,lg,xl,xxl} Breakpoint at which the sidebar transitions from off-canvas (mobile) to inline (desktop). lg is the default in the demos.
sidebar-mini Collapses sidebar to icon-only mini state
sidebar-mini sidebar-collapse Starts the page with the mini sidebar already collapsed
sidebar-without-hover Disables the auto-expand-on-hover behaviour for the mini sidebar
layout-rtl Mirrors the layout for right-to-left languages (also requires .rtl.css and dir="rtl" — see RTL Support)

These compose. The demo dashboards use class="layout-fixed sidebar-expand-lg bg-body-tertiary" — fixed-height with a desktop sidebar breakpoint at the lg (≥992px) boundary.

Layout variants demo

The Layout section of the demo site (/layout/*.html files) ships eleven pre-built combinations:

Responsive behaviour

At and above your sidebar-expand-* breakpoint, the sidebar is inline — visible in the grid alongside .app-main. Below the breakpoint, it becomes off-canvas — hidden by default and slid in from the left when the user clicks the hamburger toggle.

Mobile-specific classes:

Class When applied Effect
sidebar-open Body — when the user explicitly opens the mobile sidebar Slides the sidebar in over the content with an overlay
sidebar-collapse Body — set automatically when the user closes the mobile sidebar Hides the sidebar

These are managed by the PushMenu plugin — you don’t write them yourself.

Common pitfalls
  • Forgetting .container-fluid inside .app-content — the content lands flush against the screen edge. Wrap your content cards in a container.
  • Putting the sidebar toggle outside .app-header — it’ll still work, but the styling expects the navbar context.
  • Mixing .app-* and .main-* classes.main-* is the AdminLTE 3 naming. Don’t combine them. See Migration from v3.
  • Using position: fixed on something inside .app-mainlayout-fixed already gives .app-main overflow: auto. Children with position: fixed may behave unexpectedly. Use sticky positioning instead.
  • Hardcoding sidebar width in custom CSS — the sidebar width is exposed via the SCSS variable $lte-sidebar-width. If you need to know it from JavaScript, read the rendered .app-sidebar element’s computed width.
Where to next