Jesse A. Green / notebook

AdminLTE 4 User Guide

Generated June 2026 from ColorlibHQ/AdminLTE at 3c9e9f4

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

AdminLTE is a free, MIT-licensed admin dashboard template built on Bootstrap 5.3 with vanilla TypeScript — no jQuery. It gives you a responsive application shell (header, collapsible sidebar, content area, footer), light/dark/auto color modes, RTL support, seven ready-to-use JavaScript plugins, and a catalog of demo pages. You drop the markup into your own project and customize the parts you need.

This guide takes you from a blank HTML file to a working, customized dashboard. It is written for the people who build dashboards with AdminLTE: front-end developers, full-stack engineers wiring AdminLTE into a Laravel/Django/Rails view, and anyone integrating the template into an existing app.

Note

AdminLTE is a template, not a component library. Bootstrap 5 supplies the components (buttons, cards, modals, forms); AdminLTE supplies the application shell that wraps them — the wrapper grid, the sidebar system, the color-mode plumbing, and the dashboard widgets. If you already know Bootstrap 5, you already know 80% of AdminLTE.


Table of Contents

  1. Quick Start
  2. Installation
  3. The Layout System
  4. Building the Sidebar Navigation
  5. JavaScript Plugins
  6. Color Modes (Light / Dark / Auto)
  7. Customizing the Look
  8. Common Recipes
  9. Adding Third-Party Widgets
  10. Right-to-Left (RTL) Layouts
  11. Upgrading from AdminLTE 3
  12. Troubleshooting
  13. FAQ
  14. Reference

1. Quick Start

AdminLTE is an HTML template — there is no build step required to use it. You can have a working dashboard in about ten minutes using a CDN, without npm, without a bundler, and without learning the project's internal Astro tooling.

Step 1 — Add the CDN tags

Create a new HTML file. Put these four stylesheet tags in <head>. They pull AdminLTE, Bootstrap 5.3, OverlayScrollbars, and Bootstrap Icons from jsDelivr:

<!-- In <head> -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.13.1/font/bootstrap-icons.min.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/overlayscrollbars@2.11.0/styles/overlayscrollbars.min.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/admin-lte@4.0.0/dist/css/adminlte.min.css" />

Put these four script tags just before </body>:

<!-- Before </body> -->
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/overlayscrollbars@2.11.0/browser/overlayscrollbars.browser.es6.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/admin-lte@4.0.0/dist/js/adminlte.min.js"></script>

Important

Load order matters. AdminLTE's CSS imports Bootstrap's CSS, and AdminLTE's JavaScript must load after Bootstrap and Popper. Keep the script order exactly as shown above or interactive behavior breaks silently.

Step 2 — Drop in the minimum viable layout

This is the smallest page that still feels like AdminLTE — a header, a collapsible sidebar, and a content area. Place it inside <body>, between the CSS and JS tags:

<body class="layout-fixed sidebar-expand-lg bg-body-tertiary">
  <div class="app-wrapper">

    <!-- Header -->
    <nav class="app-header navbar navbar-expand bg-body">
      <div class="container-fluid">
        <ul class="navbar-nav">
          <li class="nav-item">
            <a class="nav-link" data-lte-toggle="sidebar" href="#" role="button">
              <i class="bi bi-list"></i>
            </a>
          </li>
        </ul>
      </div>
    </nav>

    <!-- Sidebar -->
    <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">
            <li class="nav-item">
              <a href="#" class="nav-link active">
                <i class="nav-icon bi bi-speedometer"></i>
                <p>Dashboard</p>
              </a>
            </li>
            <li class="nav-item">
              <a href="#" class="nav-link">
                <i class="nav-icon bi bi-bar-chart"></i>
                <p>Reports</p>
              </a>
            </li>
          </ul>
        </nav>
      </div>
    </aside>

    <!-- Main content -->
    <main class="app-main">
      <div class="app-content-header">
        <div class="container-fluid">
          <h3 class="mb-0">Hello, AdminLTE</h3>
        </div>
      </div>
      <div class="app-content">
        <div class="container-fluid">
          <div class="card">
            <div class="card-header"><h5 class="card-title">Welcome</h5></div>
            <div class="card-body">
              Edit this file to start building your dashboard.
            </div>
          </div>
        </div>
      </div>
    </main>

  </div>
</body>

Step 3 — Open it

Open the file in any modern browser. You should see a dashboard with a dark sidebar on a light page. Click the hamburger button (bi-list) at the top-left — the sidebar collapses. That is AdminLTE's PushMenu plugin working out of the box, wired entirely by the data-lte-toggle="sidebar" attribute. No JavaScript code of your own is needed.

Tip

Every interactive feature in AdminLTE works the same way: add a data-lte-toggle="..." attribute to a trigger element and the bundled JavaScript wires it on page load. You only write JavaScript when you want programmatic control (see Section 5).

What's actually happening

Four classes do the heavy lifting:

Class What it does
app-wrapper The CSS-grid root — defines the header / sidebar / main / footer regions
app-header The top navbar; pinned to the top of the viewport when layout-fixed is on <body>
app-sidebar The collapsible side rail; sidebar-expand-lg on <body> sets its breakpoint
app-main The scrollable content region — everything else lives here

The next sections explain each of these in full.


2. Installation

You have four installation paths. Pick the one that matches how you'll actually use AdminLTE.

Path Best for Setup time
CDN Prototyping, demos, simple sites 1 minute
npm + bundler Production apps, framework projects (Laravel, Symfony, Rails) 5 minutes
Build from source Customizing SCSS variables (sidebar width, breakpoints, brand colors) 10 minutes
Composer PHP projects that prefer Packagist over npm 2 minutes

Prerequisites

Minimum Recommended
Bootstrap 5.3.0 5.3.8 (pinned in package.json)
Node.js 18 LTS 22 LTS (used by the project's CI)
Browser Modern evergreen Latest stable Chrome / Firefox / Safari / Edge

Warning

AdminLTE 4 does not support Internet Explorer. If you need IE 11, stay on AdminLTE 3 or earlier — that constraint is not reversible in v4.

CDN — the fastest path

Use the tags from Quick Start. No install, no build step. Then copy any demo page's HTML into your own file and edit it. This is ideal for prototypes and simple internal tools.

npm + bundler

Requires Node.js 18+ and a bundler (Vite, Webpack, Rollup, esbuild, etc.).

npm

npm install admin-lte@4.0.0

yarn

yarn add admin-lte@4.0.0

pnpm

pnpm add admin-lte@4.0.0

Then import the CSS and JS in your app's entry point:

// CSS
import "admin-lte/dist/css/adminlte.min.css"
// Plus Bootstrap and dependency CSS
import "bootstrap-icons/font/bootstrap-icons.css"
import "overlayscrollbars/styles/overlayscrollbars.css"

// JS
import "bootstrap"
import "admin-lte"

Build from source

Choose this if you want to change SCSS variables — sidebar width, breakpoints, brand colors — which CSS custom properties can't reach.

git clone https://github.com/ColorlibHQ/AdminLTE.git
cd AdminLTE
npm install
npm run dev          # dev server with hot-reload at http://localhost:3000
npm run production   # one-off production build (clean + lint + compile)

The compiled output lands in dist/. Edit src/scss/_variables.scss or src/scss/_bootstrap-variables.scss, then rerun the build. See Customizing the Look for which variables matter.

Composer (PHP)

composer require "almasaeed2010/adminlte=4.0.0"

The Packagist package is published from the same source tree — you get the dist/ folder ready to serve.

Note

Whatever path you choose, the runtime artifacts are always the same two files: dist/css/adminlte.min.css and dist/js/adminlte.min.js, plus their Bootstrap and icon dependencies. AdminLTE doesn't bundle Bootstrap, Popper, OverlayScrollbars, or the icon font — you load those alongside it.


3. The Layout System

Every AdminLTE page follows the same blueprint: a wrapper containing a header, a sidebar, a main content area, and an optional footer.

The four regions

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

.app-wrapper is a CSS-grid container; the four children are grid areas positioned by their class. Order inside .app-wrapper doesn't matter — the grid template places each child by its class, so use whatever order reads cleanly.

Minimal structure

<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>

Region reference

.app-wrapper

The root container; defines the grid that positions header, sidebar, main, and footer. Always lives directly inside <body>. 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 visuals. This is where the sidebar toggle button lives:

<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. It 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" attribute on the sidebar gives the dark-sidebar-on-light-page look from the demos. Drop it for a light sidebar.

.app-main

The page content region. It 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. .app-content is everything below it. Wrap the children of both 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.

Body-level layout modifiers

Layout behavior is controlled mostly by classes on <body>, not on .app-wrapper:

Class What it does
layout-fixed Sidebar gets its own scrollbar; only .app-main scrolls with the page
fixed-header Sticky .app-header; the sidebar also pins (new in v4.0.0)
fixed-footer Sticky .app-footer at the bottom
sidebar-expand-{sm,md,lg,xl,xxl} Breakpoint at which the sidebar switches from off-canvas (mobile) to inline (desktop). lg is the demo default.
sidebar-mini Collapses the sidebar to an 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 behavior for the mini sidebar
layout-rtl Mirrors the layout for right-to-left languages (also needs .rtl.css + dir="rtl"; see Section 10)

These compose. The demo dashboards use class="layout-fixed sidebar-expand-lg bg-body-tertiary".

Responsive behavior

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, and slid in from the left when the user taps the hamburger. The PushMenu plugin manages this automatically.

Tip

The eleven prebuilt layout combinations live in the demo's layout/ folder (fixed-sidebar, fixed-header, fixed-footer, fixed-complete, unfixed-sidebar, sidebar-mini, collapsed-sidebar, collapsed-sidebar-without-hover, layout-custom-area, logo-switch, layout-rtl). Open the matching demo page to see each modifier in isolation.

Common pitfalls

Caution

  • Forgetting .container-fluid inside .app-content — content lands flush against the screen edge. Always wrap content in a container.
  • Mixing .app-* and .main-* classes — .main-* is the AdminLTE 3 naming. Don't combine them (see Section 11).
  • Using position: fixed on something inside .app-main — with layout-fixed, .app-main already has overflow: auto. Use sticky positioning instead.
  • Hardcoding sidebar width in custom CSS — the width is the SCSS variable $lte-sidebar-width. If you need it in JavaScript, read the computed width of the .app-sidebar element.

4. Building the Sidebar Navigation

The sidebar's nav menu is powered by the Treeview plugin. Add data-lte-toggle="treeview" to the parent <ul>, mark items with .nav-item, and put any submenu in a nested <ul class="nav-treeview">.

A single-level menu

<ul class="nav sidebar-menu flex-column" data-lte-toggle="treeview" role="menu">
  <li class="nav-item">
    <a class="nav-link active" href="/">
      <i class="nav-icon bi bi-speedometer"></i>
      <p>Dashboard</p>
    </a>
  </li>
  <li class="nav-item">
    <a class="nav-link" href="/reports">
      <i class="nav-icon bi bi-bar-chart"></i>
      <p>Reports</p>
    </a>
  </li>
</ul>

Mark the current page with .active on its .nav-link. For server-rendered apps, add it in your template; for SPAs, toggle it on route change (see the recipe below).

A multi-level (collapsible) menu

Add a .nav-arrow chevron and a nested .nav-treeview list. The chevron rotates when the submenu opens:

<li class="nav-item">
  <a href="#" class="nav-link">
    <i class="nav-icon bi bi-folder"></i>
    <p>
      Reports
      <i class="nav-arrow bi bi-chevron-right"></i>
    </p>
  </a>
  <ul class="nav nav-treeview">
    <li class="nav-item">
      <a href="#" class="nav-link"><i class="nav-icon bi bi-circle"></i><p>Sales</p></a>
    </li>
    <li class="nav-item">
      <a href="#" class="nav-link"><i class="nav-icon bi bi-circle"></i><p>Traffic</p></a>
    </li>
  </ul>
</li>

To start with a submenu already expanded, add menu-open to its .nav-item.

Section headers and badges

Group sections with .nav-header and show counts with a Bootstrap badge:

<li class="nav-header">REPORTS</li>
<li class="nav-item">
  <a href="#" class="nav-link">
    <i class="nav-icon bi bi-bar-chart"></i>
    <p>Sales</p>
    <span class="badge text-bg-danger ms-auto">3</span>
  </a>
</li>

Accordion vs. multi-open

By default the treeview is an accordion — opening one submenu closes its siblings. To allow multiple submenus open at once, set data-accordion="false" on the parent <ul>:

<ul class="nav sidebar-menu" data-lte-toggle="treeview" data-accordion="false">
  …
</ul>

You can also tune the slide animation. Default is 300 ms; set 0 to disable:

<ul class="nav sidebar-menu" data-lte-toggle="treeview" data-animation-speed="150">
  …
</ul>
Attribute Default Description
data-accordion true Close other open submenus when one opens
data-animation-speed 300 Slide animation duration in milliseconds (0 disables)

5. JavaScript Plugins

AdminLTE ships seven JavaScript plugins as a single bundle (adminlte.js). Each one can be triggered declaratively with a data-lte-* attribute (no code needed) and also exposes a programmatic API for code-driven control.

Plugin Data API trigger What it does
PushMenu data-lte-toggle="sidebar" Collapse / expand the sidebar
Treeview data-lte-toggle="treeview" (on the parent menu) Expand / collapse sidebar submenus
CardWidget data-lte-toggle="card-collapse", card-remove, card-maximize Collapse, remove, or maximize a card
DirectChat data-lte-toggle="chat-pane" Toggle the contacts pane of a chat widget
FullScreen data-lte-toggle="fullscreen" Toggle native browser fullscreen
Layout (auto-applied to <body>) Manage transitions during resize; signal "app loaded"
AccessibilityManager (auto-initialized) WCAG 2.1 AA features (see Section 5.7)

Two ways to use a plugin

1. Data API (declarative) — for most pages this is all you need. Drop the right attribute on the trigger and the bundle wires it up on DOMContentLoaded:

<button data-lte-toggle="sidebar">☰</button>

2. Programmatic (imperative) — when you need to control a plugin from your own code (e.g. open the sidebar after login, expand a card on a route change), instantiate the class directly. All seven are exported from the package root:

// ESM (bundler import)
import { PushMenu, CardWidget, Treeview } from "admin-lte"

new PushMenu(document.body, {}).expand()

const card = document.querySelector("#chart-card")
new CardWidget(card, {}).maximize()

Or via the global the UMD bundle assigns:

<script>
  // The bundle assigns to window.adminlte
  new adminlte.PushMenu(document.body, {}).expand()
</script>

Note

The bundle attaches all data-API listeners on DOMContentLoaded. PushMenu, CardWidget, and Treeview use event delegation, so dynamically-injected elements still work after page load.

Listening to plugin events

Every plugin fires an event on the element it operates on. All events bubble, so you can listen on document:

document.addEventListener("expanded.lte.card-widget", (e) => {
  console.log("Card expanded:", e.target)
})

document.addEventListener("open.lte.push-menu", () => {
  // user opened the sidebar — e.g. an analytics call
})
Plugin Event Fired when
PushMenu open.lte.push-menu Sidebar expanded
PushMenu collapse.lte.push-menu Sidebar collapsed
Treeview expanded.lte.treeview Submenu opened
Treeview collapsed.lte.treeview Submenu closed
Treeview load.lte.treeview A pre-opened submenu was detected on page load
CardWidget expanded.lte.card-widget Card body expanded
CardWidget collapsed.lte.card-widget Card body collapsed
CardWidget remove.lte.card-widget Card removed
CardWidget maximized.lte.card-widget Card maximized
CardWidget minimized.lte.card-widget Card restored from maximized
DirectChat expanded.lte.direct-chat Contacts pane opened
DirectChat collapsed.lte.direct-chat Contacts pane closed
FullScreen maximized.lte.fullscreen Entered fullscreen
FullScreen minimized.lte.fullscreen Exited fullscreen

5.1 PushMenu — the sidebar

Toggles the sidebar between expanded and collapsed, handling responsive behavior, optional localStorage persistence, and the mobile overlay.

Data API:

<a href="#" data-lte-toggle="sidebar">Toggle Sidebar</a>

Configuration (data attributes on the .app-sidebar element):

Attribute Default Description
data-sidebar-breakpoint 992 Screen width (px) below which the sidebar is treated as mobile
data-enable-persistence false Save sidebar state to localStorage across page loads
<aside class="app-sidebar" data-enable-persistence="true" data-sidebar-breakpoint="768">
  …
</aside>

When persistence is on, the plugin reads/writes the localStorage key lte.sidebar.state, storing either "sidebar-open" or "sidebar-collapse".

Programmatic methods:

Method Description
toggle() Expand or collapse based on current state (persists if enabled)
expand() Expand the sidebar; fires open.lte.push-menu
collapse() Collapse the sidebar; fires collapse.lte.push-menu
isCollapsed() true if <body> has sidebar-collapse
isExplicitlyOpen() true if <body> has sidebar-open (mobile, user-opened)
isMiniMode() true if <body> has sidebar-mini
isMobileSize() true if window.innerWidth ≤ sidebarBreakpoint
saveSidebarState(state) Manually persist "sidebar-open" or "sidebar-collapse"
loadSidebarState() Read and apply persisted state
clearSidebarState() Remove persisted state
updateStateByResponsiveLogic() Recompute state from viewport size (auto-called on resize)

5.2 Treeview — sidebar submenus

Covered in detail in Section 4. Programmatic API:

import { Treeview } from "admin-lte"

const item = document.querySelector(".sidebar-menu .nav-item")
const tv = new Treeview(item, { accordion: true, animationSpeed: 300 })

tv.open()    // expand this item's submenu
tv.close()   // collapse it
tv.toggle()  // toggle

5.3 CardWidget — collapsible / removable / maximizable cards

Adds collapse, remove, and maximize controls to any Bootstrap .card. Put the trigger buttons inside .card-tools in the card header:

<div class="card">
  <div class="card-header">
    <h3 class="card-title">Card Title</h3>
    <div class="card-tools">
      <button type="button" class="btn btn-tool" data-lte-toggle="card-collapse" title="Collapse">
        <i data-lte-icon="expand" class="bi bi-plus-lg"></i>
        <i data-lte-icon="collapse" class="bi bi-dash-lg"></i>
      </button>
      <button type="button" class="btn btn-tool" data-lte-toggle="card-maximize" title="Maximize">
        <i class="bi bi-fullscreen"></i>
      </button>
      <button type="button" class="btn btn-tool" data-lte-toggle="card-remove" title="Remove">
        <i class="bi bi-x-lg"></i>
      </button>
    </div>
  </div>
  <div class="card-body">Card content here.</div>
  <div class="card-footer">Card footer</div>
</div>

Programmatic methods: collapse(), expand(), toggle(), maximize(), minimize(), toggleMaximize(), remove(). Instantiate with an optional animationSpeed (default 500 ms):

import { CardWidget } from "admin-lte"
const widget = new CardWidget(document.querySelector("#chart-card"), { animationSpeed: 250 })
widget.maximize()

Note

Nested cards are handled correctly — collapsing a parent card does not affect child cards. Maximizing a card also adds maximized-card to <html> so global styles can react. A card that was collapsed before you maximize it remembers that state (tracked via the was-collapsed class).

5.4 DirectChat — chat contacts pane

Toggles a contacts pane inside a .direct-chat card. Add data-lte-toggle="chat-pane" to a button inside the chat card:

<div class="card direct-chat">
  <div class="card-header">
    <h3 class="card-title">Direct Chat</h3>
    <div class="card-tools">
      <button type="button" class="btn btn-tool" data-lte-toggle="chat-pane" title="Toggle Contacts">
        <i class="bi bi-people-fill"></i>
      </button>
    </div>
  </div>
  <div class="card-body">
    <div class="direct-chat-messages"><!-- messages --></div>
    <div class="direct-chat-contacts"><!-- contacts --></div>
  </div>
</div>

Programmatic: new DirectChat(element).toggle(). The constructor takes no config object — pass only the .direct-chat element. See the direct-chat recipe for a fuller example.

5.5 FullScreen — native browser fullscreen

Toggles the whole page into the browser's native fullscreen mode. Use data-lte-icon="maximize" and data-lte-icon="minimize" on the child icons so the right one shows:

<button type="button" class="btn" data-lte-toggle="fullscreen">
  <i data-lte-icon="maximize" class="bi bi-arrows-fullscreen"></i>
  <i data-lte-icon="minimize" class="bi bi-fullscreen-exit d-none"></i>
</button>

Programmatic: new FullScreen(button).toggleFullScreen().

Note

Fullscreen targets <html>, so the entire page goes fullscreen. To make just one card fill the screen, use the CardWidget maximize() method instead. The plugin checks document.fullscreenEnabled before acting, and toggles the icons via Bootstrap's .d-none utility so it works with any icon library (Bootstrap Icons, FontAwesome, Lucide, etc.).

5.6 Layout — transitions and load signal

This plugin self-initializes on <body> — there's nothing to wire up. It does two things automatically:

  1. Holds transitions during resize — adds hold-transition to <body> briefly so the sidebar/navbar/content don't animate awkwardly while the window is being resized.
  2. Signals app-loaded — adds app-loaded to <body> shortly after DOMContentLoaded. Use this in CSS to defer entrance animations until the layout is ready:
/* Only animate after the app is loaded */
body.app-loaded .app-sidebar {
  transition: width 0.3s ease;
}

The single public method, holdTransition(time), lets you suppress transitions around your own DOM changes:

import { Layout } from "admin-lte"
const layout = new Layout(document.body)
layout.holdTransition(200)
document.body.classList.toggle("sidebar-mini")

5.7 AccessibilityManager

AdminLTE auto-initializes a set of WCAG 2.1 AA features when it loads — you get them for free. They are configured in the bundle with everything enabled:

Feature WCAG criterion What it does
Skip links 2.4.1 Adds "Skip to main content" / "Skip to navigation" links
Focus management 2.4.3, 2.4.7 Tab cycling, focus trapping in modals, focus restoration
Keyboard navigation 2.1.1 Arrow-key navigation in menus; Enter/Space for custom role="button" elements
Reduced motion 2.3.3 Respects the OS prefers-reduced-motion setting by disabling animations
Live announcements 4.1.3 Announces error and success messages to screen readers
Error identification 3.3.1, 3.3.2 Auto-labels unlabelled inputs; announces form-validation errors
Table accessibility 1.3.1 Adds scope attributes to table headers automatically

If you need to control it, the helper initAccessibility() and the manager's public API are exported:

import { initAccessibility } from "admin-lte"

const a11y = initAccessibility({
  announcements: true,
  skipLinks: true,
  focusManagement: true,
  keyboardNavigation: true,
  reducedMotion: true
})

a11y.announce("Item saved successfully", "polite")   // screen-reader announcement
a11y.focusElement("#my-input")                        // move focus + scroll into view
a11y.trapFocus(document.querySelector(".my-dialog"))  // trap Tab inside a container
a11y.addLandmarks()                                   // add ARIA landmark roles

Set any feature flag to false to disable it.

Tip

A standalone accessibilityUtils object is also exported, with checkColorContrast(fg, bg) (returns { ratio, passes } against the 4.5:1 threshold), isFocusable(element), and generateId(prefix) helpers for building accessible UI of your own.


6. Color Modes (Light / Dark / Auto)

AdminLTE follows Bootstrap 5.3's native theming. The active theme is set by the data-bs-theme attribute — apply it to <html>, <body>, or any single element:

<!-- Whole page dark -->
<html data-bs-theme="dark">

<!-- Just the sidebar dark, page light (the demo look) -->
<aside class="app-sidebar" data-bs-theme="dark">…</aside>

A working light/dark/auto toggle

AdminLTE's demos include a dropdown toggle in the topbar that switches the attribute and remembers the choice. The markup uses data-bs-theme-value on each option:

<li class="nav-item dropdown">
  <button class="btn btn-link nav-link dropdown-toggle d-flex align-items-center"
          id="bd-theme" type="button" data-bs-toggle="dropdown" aria-expanded="false">
    <span class="theme-icon-active"><i class="my-1"></i></span>
    <span class="d-lg-none ms-2" id="bd-theme-text">Toggle theme</span>
  </button>
  <ul class="dropdown-menu dropdown-menu-end" aria-labelledby="bd-theme-text">
    <li><button type="button" class="dropdown-item d-flex align-items-center active" data-bs-theme-value="light">
      <i class="bi bi-sun-fill me-2"></i> Light <i class="bi bi-check-lg ms-auto d-none"></i></button></li>
    <li><button type="button" class="dropdown-item d-flex align-items-center" data-bs-theme-value="dark">
      <i class="bi bi-moon-fill me-2"></i> Dark <i class="bi bi-check-lg ms-auto d-none"></i></button></li>
    <li><button type="button" class="dropdown-item d-flex align-items-center" data-bs-theme-value="auto">
      <i class="bi bi-circle-half me-2"></i> Auto <i class="bi bi-check-lg ms-auto d-none"></i></button></li>
  </ul>
</li>

The accompanying script reads the preferred theme (stored choice → OS preference), writes data-bs-theme on <html>, and persists the user's selection to localStorage:

(() => {
  "use strict";
  const storedTheme = localStorage.getItem("theme");

  const getPreferredTheme = () =>
    storedTheme ||
    (window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light");

  const setTheme = (theme) => {
    if (theme === "auto" && window.matchMedia("(prefers-color-scheme: dark)").matches) {
      document.documentElement.setAttribute("data-bs-theme", "dark");
    } else {
      document.documentElement.setAttribute("data-bs-theme", theme);
    }
  };

  setTheme(getPreferredTheme());

  window.addEventListener("DOMContentLoaded", () => {
    for (const toggle of document.querySelectorAll("[data-bs-theme-value]")) {
      toggle.addEventListener("click", () => {
        const theme = toggle.getAttribute("data-bs-theme-value");
        localStorage.setItem("theme", theme);
        setTheme(theme);
      });
    }
  });
})();

Note

This color-mode toggle is a copy-paste snippet, not part of the adminlte.js bundle. The snippet persists the choice under the localStorage key theme. If you write your own toggle, make sure it both sets data-bs-theme and writes to localStorage.

Tip

The Auto option follows the OS preference live: the script listens for changes to prefers-color-scheme and re-applies the theme when the user switches their system between light and dark.


7. Customizing the Look

You can retheme AdminLTE two ways:

  1. CSS custom properties — quick, no build step, works on the prebuilt CSS.
  2. SCSS variables — requires a build, but lets you change anything, including breakpoints and sidebar width.

Quick wins with CSS variables

Bootstrap and AdminLTE expose dozens of custom properties on :root. Override them in your own stylesheet — no rebuild needed:

:root,
[data-bs-theme="light"] {
  --bs-primary: #6610f2;          /* swap Bootstrap blue for purple */
  --bs-primary-rgb: 102, 16, 242;
  --bs-body-bg: #f3f4f6;
}

[data-bs-theme="dark"] {
  --bs-body-bg: #14171c;
  --bs-body-color: #e9ecef;
}

Most of AdminLTE's chrome is built on var(--bs-primary), so it picks this up automatically.

Variable Default Controls
--bs-primary #0d6efd Brand color; active sidebar items, primary buttons
--bs-body-bg #fff / #212529 (dark) Page background
--bs-body-color #212529 Default text color
--bs-border-color #dee2e6 Hairline borders
--bs-border-radius .375rem Default radius for cards, buttons, inputs
--bs-font-sans-serif system-ui, … Default font stack

SCSS variables (build from source)

When building from source, override variables before importing AdminLTE. All AdminLTE/Bootstrap variables use !default, so the first declaration wins:

// my-theme.scss

// 1. Your overrides FIRST
$primary:           #6610f2;
$lte-sidebar-width: 280px;
$border-radius:     .25rem;

// 2. Then import AdminLTE (which imports Bootstrap)
@import "node_modules/admin-lte/src/scss/adminlte";

The most-overridden AdminLTE variables and their defaults:

// Sidebar
$lte-sidebar-width:        250px;   // collapses to a ~70px mini state
$lte-sidebar-breakpoint:   lg;      // breakpoint where the sidebar goes off-canvas
$lte-sidebar-padding-x:    .5rem;
$lte-sidebar-padding-y:    .5rem;

// Transitions
$lte-transition-speed:     .3s;
$lte-transition-fn:        ease-in-out;

Important

Sidebar width and breakpoints are SCSS-only — there are no CSS custom properties for them. If you need to change those, you must build from source. Use CSS variables for everything they cover (colors, radius, fonts), because those survive AdminLTE upgrades without merge conflicts.

When to use which

Customization CSS variables SCSS rebuild
Brand colors ✅ ✅
Body / text colors ✅ ✅
Border radius ✅ ✅
Sidebar width ❌ ✅
Breakpoint changes ❌ ✅
Spacing scale ❌ ✅

.sidebar-brand accepts text, an image, or both:

<div class="sidebar-brand">
  <a href="/" class="brand-link">
    <img src="/assets/logo.svg" alt="Logo" class="brand-image opacity-75 shadow" />
    <span class="brand-text fw-light">My Dashboard</span>
  </a>
</div>

Compact mode

Add .compact-mode to .app-wrapper (or higher) to tighten padding across the whole layout — handy for data-dense dashboards:

<div class="app-wrapper compact-mode">…</div>

8. Common Recipes

Copy-paste patterns that show up in every admin dashboard. Each assumes you have a working AdminLTE page from Quick Start.

A KPI card (small-box)

The bright stat boxes at the top of most dashboards. Swap text-bg-primary for text-bg-success, text-bg-warning, text-bg-danger, etc.

<div class="row g-3">
  <div class="col-lg-3 col-6">
    <div class="small-box text-bg-primary">
      <div class="inner">
        <h3>150</h3>
        <p>New orders</p>
      </div>
      <i class="bi bi-cart small-box-icon" aria-hidden="true"></i>
      <a href="#" class="small-box-footer link-light">
        More info <i class="bi bi-arrow-right"></i>
      </a>
    </div>
  </div>
  <!-- repeat with other values + colors -->
</div>

A page-title + breadcrumb row

The standard .app-content-header pattern:

<div class="app-content-header">
  <div class="container-fluid">
    <div class="row">
      <div class="col-sm-6"><h3 class="mb-0">Page Title</h3></div>
      <div class="col-sm-6">
        <ol class="breadcrumb float-sm-end">
          <li class="breadcrumb-item"><a href="/">Home</a></li>
          <li class="breadcrumb-item active" aria-current="page">Current page</li>
        </ol>
      </div>
    </div>
  </div>
</div>

A user dropdown in the topbar

Put this inside <ul class="navbar-nav ms-auto"> in .app-header:

<li class="nav-item dropdown user-menu">
  <a href="#" class="nav-link dropdown-toggle" data-bs-toggle="dropdown">
    <span class="d-none d-md-inline me-1">Jane Doe</span>
    <i class="bi bi-person-circle fs-5"></i>
  </a>
  <ul class="dropdown-menu dropdown-menu-end">
    <li><a href="/profile" class="dropdown-item"><i class="bi bi-person me-2"></i>Profile</a></li>
    <li><a href="/settings" class="dropdown-item"><i class="bi bi-gear me-2"></i>Settings</a></li>
    <li><hr class="dropdown-divider"></li>
    <li><a href="/logout" class="dropdown-item text-danger"><i class="bi bi-box-arrow-right me-2"></i>Sign out</a></li>
  </ul>
</li>

A responsive data table

Wrap the table in .table-responsive so it scrolls horizontally on narrow screens instead of overflowing the card:

<div class="card">
  <div class="card-header"><h3 class="card-title">Recent orders</h3></div>
  <div class="card-body p-0">
    <div class="table-responsive">
      <table class="table table-striped align-middle mb-0">
        <thead>
          <tr><th>#</th><th>Customer</th><th>Total</th><th>Status</th></tr>
        </thead>
        <tbody><!-- rows --></tbody>
      </table>
    </div>
  </div>
</div>

For sorting/filtering/pagination, use Tabulator.

A direct-chat widget

<div class="card direct-chat direct-chat-primary">
  <div class="card-header">
    <h3 class="card-title">Direct Chat</h3>
    <div class="card-tools">
      <span class="badge text-bg-primary">3 new</span>
      <button type="button" class="btn btn-tool" data-lte-toggle="chat-pane" title="Contacts">
        <i class="bi bi-people-fill"></i>
      </button>
    </div>
  </div>
  <div class="card-body">
    <div class="direct-chat-messages">
      <div class="direct-chat-msg">
        <div class="direct-chat-infos clearfix">
          <span class="direct-chat-name float-start">Olivia</span>
          <span class="direct-chat-timestamp float-end">12:05</span>
        </div>
        <img class="direct-chat-img" src="/avatars/olivia.jpg" alt="" />
        <div class="direct-chat-text">Hey, ready for the review?</div>
      </div>
    </div>
    <div class="direct-chat-contacts"><!-- contacts, shown when toggled --></div>
  </div>
</div>

Highlighting the active sidebar item from JavaScript

For a single rendered sidebar with client-side route highlighting:

function setActiveSidebarItem(href) {
  document.querySelectorAll(".sidebar-menu .nav-link").forEach((el) => {
    el.classList.toggle("active", el.getAttribute("href") === href)
  })
}

setActiveSidebarItem(window.location.pathname)  // call on every route change

Refitting a chart when the sidebar collapses

Charts read their container width on init, so re-measure them after the sidebar transition finishes:

document.addEventListener("collapse.lte.push-menu", () => {
  setTimeout(() => chart.updateOptions({}), 350)  // ~300ms transition + buffer
})
document.addEventListener("open.lte.push-menu", () => {
  setTimeout(() => chart.updateOptions({}), 350)
})

The same pattern works for Tabulator, FullCalendar, and any width-sensitive widget.


9. Adding Third-Party Widgets

AdminLTE deliberately ships a lean dependency tree — it doesn't bundle date pickers, charts, or rich-text editors. Instead, drop in best-in-class third-party libraries when you need them. Every library below is MIT-licensed, actively maintained, and jQuery-free.

Need Library One-line wire-up
Date / time picker Flatpickr flatpickr("#datepicker", { dateFormat: "Y-m-d" })
Multi-select / tags Tom Select new TomSelect("#tags", { plugins: ["remove_button"] })
Range slider noUiSlider noUiSlider.create(el, { start: [20, 80], range: { min: 0, max: 100 } })
Color picker Pickr Pickr.create({ el: "#color-picker", theme: "classic" })
Input mask IMask IMask(el, { mask: "(000) 000-0000" })
File upload Dropzone / FilePond new Dropzone("#uploader", { maxFilesize: 5 })
Rich text Quill new Quill("#editor", { theme: "snow" })
Markdown EasyMDE new EasyMDE({ element: el })
Charts ApexCharts / Chart.js new ApexCharts(el, options).render()
Data tables Tabulator new Tabulator("#table", { layout: "fitColumns" })
Calendar FullCalendar new FullCalendar.Calendar(el, { initialView: "dayGridMonth" }).render()
Drag & drop SortableJS new Sortable(el, { animation: 150 })
Lightbox GLightbox GLightbox()

The v4 demo pages already use several of these: ApexCharts (dashboard), Tabulator (tables/data), FullCalendar (pages/calendar), and SortableJS (pages/kanban). Each library ships a Bootstrap 5 theme where relevant (Tom Select, Tabulator), so it matches AdminLTE's form controls.

Icon libraries

AdminLTE ships Bootstrap Icons by default (~1,900 SVG glyphs, MIT). All of these coexist with AdminLTE if you want more — just load the font and change the <i> classes:

Library Icons Notes
Bootstrap Icons 1,900+ Default, SVG, MIT
Lucide 1,500+ ISC, very consistent style
Tabler Icons 5,800+ MIT, broadest free set
Material Symbols 3,500+ Apache 2.0, variable font
FontAwesome Free 2,000+ Font-based
Phosphor 6,000+ MIT, six weights

Tip

For production, prefer npm + a bundler (for tree-shaking and version locking) or self-hosting the minified files over relying on a public CDN long-term. Pin a specific version in any CDN URL so snippets stay reproducible.


10. Right-to-Left (RTL) Layouts

AdminLTE ships a pre-built RTL stylesheet for Arabic, Hebrew, Persian, Urdu, and other right-to-left scripts. Every release publishes RTL variants of every CSS file:

dist/css/adminlte.css           ← LTR (default)
dist/css/adminlte.rtl.css       ← RTL
dist/css/adminlte.min.css
dist/css/adminlte.rtl.min.css

To switch a page to RTL, load the .rtl stylesheet and set dir="rtl" on <html>:

<html lang="ar" dir="rtl">
  <head>
    <link rel="stylesheet" href="dist/css/adminlte.rtl.min.css" />
  </head>
  <body class="layout-fixed sidebar-expand-lg bg-body-tertiary">
    <!-- same markup as LTR -->
  </body>
</html>

That's all — the sidebar docks to the right, scrollbars flip, and Bootstrap's .ms-* / .float-start utilities mirror automatically (setting dir="rtl" triggers Bootstrap 5.3's logical-property pathway). For a mixed-language page, scope dir="rtl" to a single subtree instead of swapping the whole stylesheet.

Flips automatically Does not flip
margin-left / padding-left → right Directional icons (bi-arrow-right etc.) — swap manually
left / right positioning Inline images, charts, third-party widgets (check each for an RTL option)
text-align: left / right Numbers (digits stay LTR — standard Unicode behavior)
Sidebar position; dropdown open direction

Caution

The minified RTL build is named adminlte.rtl.min.css, not adminlte.min.rtl.css — filename order matters. And don't load both LTR and RTL stylesheets on the same page; pick one.


11. Upgrading from AdminLTE 3

AdminLTE 4 is a ground-up rewrite on Bootstrap 5.3 with no jQuery. If you're upgrading a v3 project, these are the changes most likely to bite.

AdminLTE 3 AdminLTE 4
CSS framework Bootstrap 4.6 Bootstrap 5.3
JavaScript jQuery + vanilla Vanilla TypeScript only
Dark mode .dark-mode body class data-bs-theme attribute
RTL Separate build Auto-generated via rtlcss
Browsers IE 11 forks available Modern evergreen only
Icons FontAwesome Bootstrap Icons

Class renames (the bulk of the work)

AdminLTE 3 AdminLTE 4
.wrapper .app-wrapper
.main-header .app-header
.main-sidebar .app-sidebar
.content-wrapper .app-main
.main-footer .app-footer
.content-header .app-content-header
.content .app-content
data-widget="pushmenu" data-lte-toggle="sidebar"
data-widget="treeview" data-lte-toggle="treeview"
data-widget="card-widget" data-lte-toggle="card-collapse" / card-remove / card-maximize
data-widget="fullscreen" data-lte-toggle="fullscreen"
.dark-mode (on <body>) data-bs-theme="dark"

You'll also need the Bootstrap 4→5 utility renames (data-toggle → data-bs-toggle, .ml-*/.mr-* → .ms-*/.me-*, .float-left/.float-right → .float-start/.float-end, .font-weight-* → .fw-*, etc.) — these come from Bootstrap, not AdminLTE, and a regex find-replace handles most of them.

jQuery removal

AdminLTE 3 plugin calls like $('.sidebar').PushMenu() no longer exist. Either use the data API (which does it for you), or import the plugin class:

import { PushMenu } from "admin-lte"
new PushMenu(document.querySelector(".sidebar-toggle"), {}).toggle()

Suggested order

  1. Branch first — keep master on v3 until v4 is stable.
  2. Run the class-rename find-replace pass.
  3. Update data-toggle → data-bs-toggle across all templates.
  4. Replace .dark-mode toggle code with data-bs-theme writes.
  5. Audit jQuery calls — remove them or import plugin classes.
  6. Re-test responsive breakpoints (Bootstrap 5 adds xxl).

See the project's full Migration from v3 guide for the complete list.


12. Troubleshooting

Error: the sidebar doesn't collapse when I click the hamburger

Cause: PushMenu isn't loaded, or the trigger uses the v3 attribute. Solution: Confirm adminlte.js (or adminlte.min.js) is on the page and after the markup it operates on. Verify your hamburger button uses data-lte-toggle="sidebar" — not the v3 data-widget="pushmenu". Prevention: Keep the script tags at the end of <body> in the order shown in Quick Start.

Error: dark mode doesn't persist after refresh

Cause: Your toggle isn't writing to localStorage, or isn't reading it on load. Solution: Use the Color Mode toggle snippet, which persists under the localStorage key theme. A custom toggle must both call document.documentElement.setAttribute('data-bs-theme', …) and write the choice to localStorage on change.

Error: pressing Escape closes a Bootstrap modal and the sidebar

Cause: A pre-release (RC) build. Solution: Update to the latest 4.0.0 — this was fixed (issue #5993). AdminLTE's accessibility module now defers Escape handling inside open modals to Bootstrap.

Error: my custom SCSS doesn't override AdminLTE's defaults

Cause: Your overrides come after AdminLTE's @import. Solution: Put your variable overrides before the import. AdminLTE variables use !default, so the first declaration wins (see Section 7).

Error: npm install fails with peer dependency errors

Cause: An npm older than 8.3, which ignores the overrides block in package.json. Solution: Upgrade to npm 8.3+ (which respects overrides), or install with --legacy-peer-deps as a fallback.

Error: Web Font CORS error loading FontAwesome all.min.css

Cause: Loading an icon font cross-origin without proper CORS headers (reported in issue #6008). Solution: AdminLTE 4 standardizes on Bootstrap Icons, which is SVG-based and avoids the web-font CORS class of problems. If you bring your own FontAwesome, self-host it or use a CDN that serves the correct Access-Control-Allow-Origin headers for font files.

Error: the fullscreen button shifts the icon's position

Cause: A pre-4.0.0 build that toggled inline display on the icon. Solution: Update to 4.0.0 — the FullScreen plugin now toggles Bootstrap's .d-none utility instead (issue #6021), which works with any icon library.

Error: content lands flush against the screen edge

Cause: Missing .container-fluid inside .app-content. Solution: Wrap your content in <div class="container-fluid"> (or .container for capped width).


13. FAQ

Q: What is AdminLTE, exactly? A free, MIT-licensed admin dashboard template — HTML, CSS, and JavaScript built on Bootstrap 5.3. It is not a framework or an npm component library. You drop it into your project, edit the markup to suit your app, and ship.

Q: Do I need a build step to use it? No. The CDN-only approach in Quick Start needs no install or build. The npm workflow is there if you want to customize SCSS variables or tree-shake the JavaScript.

Q: Which Bootstrap version does v4 require? Bootstrap 5.3.x (the current package.json pins 5.3.8). Bootstrap 5.0/5.1/5.2 will not work — the color-mode and data-bs-theme system landed in 5.3.

Q: Can I use AdminLTE 4 with jQuery? You don't need it — AdminLTE 4's JavaScript is vanilla. If your app already uses jQuery, the two coexist fine, but none of AdminLTE's plugins call into it and there are no $.fn.xxx() shims.

Q: Can I use it with WordPress / Laravel / Symfony / Django / Rails? Yes — it's server-rendered HTML. Copy the demo pages into your views/templates, replace static content with your framework's template variables, and wire up routes and auth as usual. Community Composer packages exist for Laravel and Symfony, but they often lag upstream — verify they ship v4 before depending on them.

Q: React / Vue / Svelte / Angular? Workable but less natural — AdminLTE's plugins use DOM lifecycle hooks that fight SPA reconciliation. The recommended approach is to use AdminLTE for its CSS and write your own framework components for the interactive parts, or pick a component library built for your framework.

Q: How do I change the primary color? Override --bs-primary (and --bs-primary-rgb) on :root — no rebuild needed. See Customizing the Look.

Q: Can I use FontAwesome instead of Bootstrap Icons? Yes. Nothing in AdminLTE requires Bootstrap Icons. Load FontAwesome's stylesheet and replace <i class="bi bi-x"> with <i class="fas fa-x">.

Q: Is AdminLTE free for commercial use? Yes — MIT licensed. Use it in commercial products, SaaS apps, client work, anything. The only requirement is preserving the copyright notice in the source files (adminlte.css / adminlte.js). No UI attribution is required.

Q: Can I sell a product made with AdminLTE? Yes — the MIT license explicitly permits it, including reselling modified versions of AdminLTE itself, as long as you keep the original copyright notice.

Q: How do I get notified of new versions? Watch the GitHub repo in Releases-only mode, or subscribe to the RSS feed at https://github.com/ColorlibHQ/AdminLTE/releases.atom.


14. Reference

Plugin data-API attributes

Attribute Plugin Goes on
data-lte-toggle="sidebar" PushMenu A button/link in the header
data-lte-toggle="treeview" Treeview The parent <ul class="sidebar-menu">
data-lte-toggle="card-collapse" CardWidget A button in .card-tools
data-lte-toggle="card-remove" CardWidget A button in .card-tools
data-lte-toggle="card-maximize" CardWidget A button in .card-tools
data-lte-toggle="chat-pane" DirectChat A button in a .direct-chat card
data-lte-toggle="fullscreen" FullScreen Any button
data-lte-icon="maximize" / "minimize" FullScreen Icons inside the fullscreen button
data-lte-icon="expand" / "collapse" CardWidget Icons inside the collapse button

Plugin config attributes

Attribute Default Plugin Goes on
data-sidebar-breakpoint 992 PushMenu .app-sidebar
data-enable-persistence false PushMenu .app-sidebar
data-accordion true Treeview the parent <ul>
data-animation-speed 300 Treeview the parent <ul>

CSS classes the plugins set

Class Set by On Meaning
sidebar-collapse PushMenu <body> Sidebar collapsed
sidebar-open PushMenu <body> Mobile sidebar explicitly open
sidebar-mini PushMenu <body> Mini-sidebar mode
menu-open Treeview .nav-item Submenu expanded
collapsed-card CardWidget .card Card body collapsed
maximized-card CardWidget <html> + .card Card maximized
direct-chat-contacts-open DirectChat .direct-chat Contacts pane visible
hold-transition Layout <body> Transitions briefly disabled
app-loaded Layout <body> Initial load animation finished
reduce-motion AccessibilityManager <body> OS prefers-reduced-motion detected

npm scripts (when building from source)

Command What it does
npm start / npm run dev Dev server with file watching at http://localhost:3000
npm run build Build all assets (clean + compile)
npm run production Full production build with linting + bundlewatch
npm run css Build CSS only (compile → prefix → RTL → minify)
npm run js Build JavaScript only (compile → minify)
npm run lint Run all linters (JS, CSS, docs, lockfile)

Runtime dependencies (loaded alongside AdminLTE)

Dependency Version (pinned) Why
Bootstrap 5.3.8 The underlying component framework
Popper 2.11.8 Bootstrap dropdowns / tooltips / popovers
Bootstrap Icons 1.13.1 Default icon set
OverlayScrollbars 2.11.0 The sidebar's custom scrollbar

Browser support

AdminLTE 4 supports the same range as Bootstrap 5 — the latest stable releases of all major browsers. From the project's .browserslistrc: Chrome >= 60, Firefox >= 60, iOS >= 12, Safari >= 12, not Explorer <= 11.