Skip to content

Assets & build

The front-end CSS/JS can be delivered two ways, selected by ADMINLTE["assets_mode"].

Vite (default)

A full django-vite pipeline with HMR — the right choice when you want to customise SCSS or add JS plugins.

python manage.py adminlte_install   # copy assets/app.js, assets/app.scss, vite.config.js stubs
npm install
npm run dev                          # dev server with HMR (when DEBUG=True)
# production:
npm run build
python manage.py collectstatic

app.js keeps AdminLTE/Bootstrap in the always-loaded core and code-splits every optional plugin (Chart.js, jsVectorMap, Tabulator, Quill, SortableJS, FullCalendar) behind dynamic imports — install only the ones you use, and each is fetched only on pages that need it. The Tool components load their plugin automatically when present in the DOM; page scripts opt in explicitly:

document.addEventListener("DOMContentLoaded", async () => {
  // Chart.js with the AdminLTE theme preset applied (light/dark aware).
  const { setupCharts, chartColor, verticalGradient } = await import("./adminlte-charts.js");
  const Chart = setupCharts();
  new Chart(canvas, {
    type: "line",
    data: { labels, datasets: [{ data, fill: "origin",
      borderColor: () => chartColor("primary"),              // follows the theme
      backgroundColor: verticalGradient("primary") }] },
  });
});

(The demo wraps the same module in its adminlteUse("chartjs") loader.) Pass colours as functions — () => chartColor("primary") — so an existing chart picks up the new colours when the Light/Dark toggle changes.

Production static storage

Vite already content-hashes everything it builds (assets/app-CjSNY8Oz.js), and its chunks import each other by those names. Django's ManifestStaticFilesStorage and WhiteNoise's CompressedManifestStaticFilesStorage hash the files again (app-CjSNY8Oz.4f2a….js) and {% vite_asset %} loads that name, but the imports inside the JavaScript are not rewritten. A lazily loaded chunk that imports the entry (Vite puts code shared with the entry there — the Quill editor chunk does) then fetches ./app-CjSNY8Oz.js: a different URL, so the browser runs a second copy of the app and every handler — the sidebar toggle, the colour-mode switch — fires twice.

Use the package's Vite-aware storages in production. They find the Vite manifest among the collected files and keep every file it lists under its own name; everything else is hashed as usual:

STORAGES = {
    "default": {"BACKEND": "django.core.files.storage.FileSystemStorage"},
    "staticfiles": {
        # WhiteNoise (compressed + manifest):
        "BACKEND": "django_adminlte4.storage.ViteCompressedManifestStaticFilesStorage",
        # without WhiteNoise:
        # "BACKEND": "django_adminlte4.storage.ViteManifestStaticFilesStorage",
    },
}

# With WhiteNoise serving static files: far-future cache headers for the
# Vite-hashed files too. Assign the function itself (a string is a regex).
from django_adminlte4.storage import immutable_file_test
WHITENOISE_IMMUTABLE_FILE_TEST = immutable_file_test

If you write your own storage, mix in django_adminlte4.storage.ViteManifestFilesMixin before the manifest storage class.

Static (Node-optional)

Serve the pre-built bundle shipped in the package — zero Node/npm:

ADMINLTE = {"assets_mode": "static"}

Then just python manage.py collectstatic. The bundle (static/adminlte/dist/) includes AdminLTE + Bootstrap + Bootstrap Icons + OverlayScrollbars CSS/JS plus a small init.js (color-mode toggle + sidebar scrollbar). django-vite is not imported in static mode, so it isn't even a required dependency for this path.

The themed Django admin always uses this pre-built bundle.

How it's wired

master.html / auth-master.html include _assets_vite.html or _assets_prebuilt.html based on assets_mode. Override {% block adminlte_assets %} for full control.

RTL

Set ADMINLTE["layout_rtl"] = True to load the prebuilt RTL stylesheet and flip the layout direction.