JavaScript Plugins Overview

AdminLTE ships eight JavaScript plugins as a single bundle (adminlte.js). Each one is exported from the package root, can be triggered via data-lte-* attributes for declarative use, and exposes a programmatic API for code-driven control.

At a glance
PluginData APIProgrammaticDocumentation
PushMenudata-lte-toggle="sidebar".toggle() / .expand() / .collapse()Reference
Treeviewdata-lte-toggle="treeview" on parent menu.toggle() / .open() / .close()Reference
CardWidgetdata-lte-toggle="card-collapse", card-remove, card-maximize.toggle() / .collapse() / .expand() / .remove() / .maximize() / .minimize() / .toggleMaximize()Reference
DirectChatdata-lte-toggle="chat-pane".toggle()Reference
FullScreendata-lte-toggle="fullscreen".toggleFullScreen() / .inFullScreen() / .outFullscreen()Reference
SidebarSearchdata-lte-toggle="sidebar-search" on an input.search(term) / .clear()Reference
Layout(auto-applied to <body>).holdTransition(time)Reference
AccessibilityManager(helper function: initAccessibility()).announce() / .focusElement() / .trapFocus() / .addLandmarks()Reference
Two ways to use them
1. Data API (declarative)

For most pages, the data API is enough — no JavaScript code required. Drop the right data-lte-* attribute on the trigger element and the bundle wires it up on page load:

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

<!-- Card collapse / remove / maximize -->
<div class="card">
  <div class="card-header">
    <h3 class="card-title">Title</h3>
    <div class="card-tools">
      <button class="btn btn-tool" data-lte-toggle="card-collapse" aria-label="Collapse card">
        <i data-lte-icon="expand" class="bi bi-dash-lg"></i>
        <i data-lte-icon="collapse" class="bi bi-plus-lg"></i>
      </button>
    </div>
  </div>
  <div class="card-body">…</div>
</div>

The bundle attaches all data-API listeners on DOMContentLoaded. Dynamically-injected elements still work for the PushMenu, CardWidget, Treeview, and SidebarSearch plugins, which use event delegation.

2. Programmatic (imperative)

When you need to control a plugin from your own code — eg. open the sidebar after a successful login, or expand a card on a route change — retrieve the instance the data API already created, or create one on demand (since 4.1):

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

// Sidebar: the data API creates the instance on load — just fetch it
const sidebar = document.querySelector(".app-sidebar")
PushMenu.getInstance(sidebar)?.expand()

// Card maximize: reuses the existing instance, or creates one
const card = document.querySelector("#chart-card")
CardWidget.getOrCreateInstance(card).maximize()

Or use the globals (UMD bundle, no build step):

<script>
  // The bundle assigns to window.adminlte
  adminlte.PushMenu.getInstance(document.querySelector(".app-sidebar"))?.expand()
</script>
Component lifecycle

Every component follows the same Bootstrap-style contract:

  • Component.getInstance(element) — the instance created for that element, or null.
  • Component.getOrCreateInstance(element, config?) — reuse or create (config only applies on creation).
  • instance.dispose() — unregister the instance; getInstance() returns null afterwards.

One instance is kept per element in a registry backed by a WeakMap, so instances are garbage-collected together with their elements (nothing to clean up on Hotwired Turbo navigations). The data API listens with delegated document-level handlers, which means toggles inside content inserted after page load — AJAX partials, Turbo Frames — work without any re-initialisation.

Listening to plugin events

Every plugin fires bubbling CustomEvents on its root element (the card, the nav item, the sidebar) — listen on document for global hooks or on the element itself for scoped ones. Actions with a “before” event are cancelable: call preventDefault() to veto them.

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

// Veto card removal until the user confirmed
document.addEventListener("remove.lte.card-widget", (e) => {
  if (!confirm("Remove this card?")) {
    e.preventDefault()
  }
})
Event name reference
PluginBefore (cancelable)AfterFired for
PushMenuopen.lte.push-menuopened.lte.push-menuSidebar expanding
PushMenucollapse.lte.push-menucollapsed.lte.push-menuSidebar collapsing
Treeviewexpand.lte.treeviewexpanded.lte.treeviewSubmenu opening (after fires post-animation)
Treeviewcollapse.lte.treeviewcollapsed.lte.treeviewSubmenu closing (after fires post-animation)
Treeview—load.lte.treeviewPre-opened submenu detected on page load
CardWidgetexpand.lte.card-widgetexpanded.lte.card-widgetCard expanding (after fires post-animation)
CardWidgetcollapse.lte.card-widgetcollapsed.lte.card-widgetCard collapsing (after fires post-animation)
CardWidgetremove.lte.card-widgetremoved.lte.card-widgetCard removal (the card leaves the DOM after removed)
CardWidget—maximized.lte.card-widgetCard maximized
CardWidget—minimized.lte.card-widgetCard minimized
DirectChat—expanded.lte.direct-chatContacts pane opened
DirectChat—collapsed.lte.direct-chatContacts pane closed
FullScreen—maximized.lte.fullscreenEntered fullscreen
FullScreen—minimized.lte.fullscreenExited fullscreen
SidebarSearch—filtered.lte.sidebar-searchMenu filtered (detail: { query, matches }; matches is -1 on clear)
ColorMode—changed.lte.color-modeTheme changed (detail: { theme, resolved }, fired on document)

All events bubble (since 4.1). The “after” events of animated actions fire when the animation completes, not when it starts.

Configuring via data attributes

Some plugins read config from data-* attributes on their target element:

<!-- Treeview — non-accordion (multiple submenus can be open at once) -->
<ul class="nav sidebar-menu" data-lte-toggle="treeview" data-accordion="false">…</ul>

<!-- Treeview — custom animation speed -->
<ul class="nav sidebar-menu" data-lte-toggle="treeview" data-animation-speed="500">…</ul>

<!-- Sidebar — opt into localStorage persistence (default: off) -->
<aside class="app-sidebar" data-enable-persistence="true">…</aside>

<!-- Sidebar — override the mobile breakpoint -->
<aside class="app-sidebar" data-sidebar-breakpoint="768">…</aside>

Each plugin’s reference page documents which attributes it supports.

CSS classes the plugins manage

The plugins toggle a small set of CSS classes that you can also style or react to:

ClassSet byWhereMeaning
sidebar-collapsePushMenu<body>Sidebar collapsed (desktop mini state, or mobile-closed)
sidebar-openPushMenu<body>Mobile sidebar explicitly opened by user
sidebar-miniPushMenu<body>Mini-sidebar mode active
menu-openTreeview.nav-itemSubmenu is currently expanded
collapsed-cardCardWidget.cardCard body/footer are collapsed
maximized-cardCardWidget<html> and .cardCard is in fullscreen mode
direct-chat-contacts-openDirectChat.direct-chatContacts pane visible
hold-transitionLayout<body>Transitions disabled briefly (during resize, etc.)
app-loadedLayout<body>Initial page-load animation finished
reduce-motionAccessibilityManager<body>OS prefers-reduced-motion detected
menu-openSidebarSearch.nav-itemSubmenu expanded to reveal a match (restored on clear)
Production vs source

The plugins live in src/ts/ as TypeScript modules. The published dist/js/adminlte.js is a Rollup bundle of all eight, exporting them under a single adminlte namespace (UMD) or named imports (ESM).

If you only need one or two plugins and care about bundle size, importing individual modules from node_modules/admin-lte/src/ts/ will tree-shake — but you’ll need TypeScript in your toolchain.

Where to next
  • Detailed reference for each plugin (links in the table above)
  • Layout Blueprint — the structural classes the plugins operate on
  • Accessibility — keyboard navigation, focus trapping, ARIA helpers