Plugin components

[object Object]

Plugin components wrap heavy third-party libraries (charts, calendars, data grids, editors, drag-and-drop). They are exported from the @adminlte/vue/plugins entry — separate from the core components — so the plugin libs stay out of the default import. Each wrapper renders a host element, then await import(...)s its library inside onMounted and destroys the instance in onBeforeUnmount.

Installation and usage

Because these wrappers load their library only in the browser, the consuming app must install the matching peer dependency and its CSS, and the component must run client-side only.

Install the peer lib + CSS

Each wrapper depends on its own (optional) peer library. Install only the ones you use, and import the library's stylesheet yourself.

# install the peer lib for the wrapper(s) you use
pnpm add chart.js            # LteChart, LteSparklineChart
pnpm add @fullcalendar/core @fullcalendar/daygrid @fullcalendar/interaction  # LteCalendar
pnpm add tabulator-tables    # LteDatatable
pnpm add quill               # LteEditor
pnpm add tom-select          # LteTomSelect
pnpm add flatpickr           # LteFlatpickr
pnpm add jsvectormap         # LteVectorMap
pnpm add sortablejs          # LteSortable, LteKanban

Wrap in <ClientOnly>

In a Nuxt (SSR) app, wrap every plugin component in <ClientOnly> so it only mounts in the browser. A #fallback keeps the layout stable during hydration.

<template>
  <ClientOnly>
    <LteChart type="line" :data="data" />
    <template #fallback>
      <div style="height: 350px" />
    </template>
  </ClientOnly>
</template>

<script setup lang="ts">
const data = {
  labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
  datasets: [{ label: 'Sales', data: [30, 40, 35, 50, 49, 60, 70] }],
}
</script>

LteChart

Wraps Chart.js 4 (MIT). It renders a sized box with a <canvas>, loads Chart.js in onMounted, and hands it { type, data, options, plugins } — the standard Chart.js config — with options merged over { responsive: true, maintainAspectRatio: false }. The chart fills the box and follows its container, so it resizes with the window, the sidebar toggle and collapsed cards. It is destroyed on unmount.

data is watched by reference: replace the object immutably (data = { ...data, datasets: [...] }) to trigger an update. Set deep-watch if you mutate it in place instead — that trades a recursive traversal of the dataset on every change for the convenience. options changes are picked up deeply; a new type or plugins list re-creates the chart. The component copies data and options before passing them on, so Chart.js never writes into your (reactive) objects.

Props

NameTypeDefaultDescription
dataChartData— (required)Chart.js data: { labels, datasets }.
typeChartType'line'line, bar, doughnut, pie, polarArea, radar, scatter or bubble.
optionsChartOptionsundefinedChart.js options.
pluginsPlugin[]undefinedInline, per-chart Chart.js plugins.
heightnumber | string350Height of the chart box (number = px).
widthnumber | stringundefinedWidth of the chart box; fills the parent when unset.
ariaLabelstringundefinedAccessible name for the canvas.
deepWatchbooleanfalseWatch data deeply (for in-place mutation).

The component exposes getChart(), which returns the live Chart.js instance (or null before mount).

<LteChart
  type="bar"
  :data="{
    labels: ['Q1', 'Q2', 'Q3', 'Q4'],
    datasets: [{ label: 'Net Profit', data: [44, 55, 57, 56] }],
  }"
  :options="{ plugins: { legend: { display: false } } }"
/>

Theme, dark mode and RTL

The first chart applies the AdminLTE theme preset to Chart.defaults: the font, text and gridline colours come from the Bootstrap CSS variables (--bs-body-font-family, --bs-body-color, --bs-secondary-color, --bs-border-color-translucent, …), vertical gridlines and axis borders are off, bars are rounded, lines are smooth, and the tooltip and legend match Bootstrap's popovers. Datasets without a colour of their own get the Bootstrap palette (--bs-primary, --bs-teal, --bs-warning, --bs-pink, …).

Charts watch data-bs-theme and dir on <html>: switching the colour mode (or direction) re-reads the variables and redraws every mounted chart, and dir="rtl" flips the legend and tooltip.

Canvas cannot read CSS variables, so resolve them yourself when you pass colours. The plugins entry exports the helpers the demo uses:

import { areaGradient, resolveColor, withAlpha } from '@adminlte/vue/plugins'

const dataset = {
  label: 'Visitors',
  data: [31, 40, 28, 51, 42, 109, 100],
  borderColor: resolveColor('var(--bs-primary)'),
  backgroundColor: areaGradient('var(--bs-primary)'), // vertical fade, re-evaluated on redraw
  fill: 'start',
}

applyChartTheme(Chart) and subscribeChartTheme(Chart, rebuild) are exported too, for charts you build with Chart.js directly.

LteCalendar

Wraps FullCalendar with the daygrid and interaction plugins. The header toolbar and editable: true are preset; pass options to override or extend the FullCalendar config. Updating events reloads the event sources (watched by reference — replace the array immutably, or set deep-watch); changing initialView after mount calls calendar.changeView(), and changed options keys are applied via calendar.setOption().

Props

NameTypeDefaultDescription
eventsEventInput[][]FullCalendar event inputs.
initialViewstring'dayGridMonth'The calendar view (reactive after mount).
optionsPartial<CalendarOptions>undefinedExtra FullCalendar options spread over the defaults.
deepWatchbooleanfalseWatch events deeply (for in-place mutation).

Emits

NamePayloadDescription
dateClickDateClickArgA calendar date was clicked.
eventClickEventClickArgAn event was clicked.
eventDropEventDropArgAn event was dragged to a new date.

Exposed

NameReturnsDescription
getApi()Calendar | nullThe underlying FullCalendar instance (null until mounted).
<template>
  <ClientOnly>
    <LteCalendar ref="cal" :events="events" @event-click="onEventClick" />
  </ClientOnly>
</template>

<script setup lang="ts">
const cal = ref()
const events = [{ title: 'Meeting', date: '2026-06-01' }]
function onEventClick(arg) {
  console.log(arg.event.title)
}
</script>

LteDatatable

Wraps Tabulator. The host options default to { layout: 'fitColumns', height, columns, data }, with options merged over the top. Updating data calls Tabulator's replaceData (watched by reference — replace the array immutably, or set deep-watch); updating columns calls setColumns. options and height are mount-only — use getTable() for runtime tweaks.

Props

NameTypeDefaultDescription
columnsArray<Record<string, unknown>>[]Tabulator column definitions (reactive after mount).
dataunknown[][]Row data.
optionsRecord<string, unknown>undefinedExtra Tabulator options merged over { columns, data }. Mount-only.
heightstringundefinedTable height. Mount-only.
deepWatchbooleanfalseWatch data deeply (for in-place mutation).

Emits

NamePayloadDescription
readytableFired on Tabulator's tableBuilt; receives the Tabulator instance.

Exposed

NameReturnsDescription
getTable()Tabulator | nullThe underlying Tabulator instance (null until mounted).
<LteDatatable
  :columns="[{ title: 'Name', field: 'name' }, { title: 'Age', field: 'age' }]"
  :data="[{ name: 'Ada', age: 36 }, { name: 'Linus', age: 54 }]"
  @ready="(table) => console.log(table)"
/>

LteSortable

A generic drag-to-reorder list backed by SortableJS. It renders a configurable host tag containing your default slot and emits update:modelValue with the reordered array. Use v-model to bind the list.

Props

NameTypeDefaultDescription
modelValueT[][]v-model — the ordered list.
tagstring'div'The host element tag to render.
optionsOptions (sortablejs)undefinedExtra SortableJS options (merged over { animation: 150 }).

Emits

NamePayloadDescription
update:modelValueT[]The reordered list.
endSortableEventThe underlying SortableJS onEnd event.
<template>
  <LteSortable v-model="items" tag="ul" class="list-group">
    <li v-for="item in items" :key="item" class="list-group-item">{{ item }}</li>
  </LteSortable>
</template>

<script setup lang="ts">
const items = ref(['Alpha', 'Bravo', 'Charlie'])
</script>

LteKanban

A multi-column drag-and-drop board built on SortableJS (shared group: 'kanban', so cards move between columns). It renders Bootstrap cards per column and emits move when a card is dropped. The KanbanCard and KanbanColumn types are exported from @adminlte/vue/plugins.

Types

interface KanbanCard {
  id: string
  title: string
  text?: string
}
interface KanbanColumn {
  id: string
  title: string
  theme?: BootstrapTheme
  cards: KanbanCard[]
}

Props

NameTypeDefaultDescription
columnsKanbanColumn[]— (required)The board columns and their cards.

Emits

NamePayloadDescription
move{ card: string; from: string; to: string; newIndex: number }A card was moved; identifies the card and source/target column ids and its new index.
<template>
  <LteKanban :columns="columns" @move="onMove" />
</template>

<script setup lang="ts">
import type { KanbanColumn } from '@adminlte/vue/plugins'

const columns = ref<KanbanColumn[]>([
  { id: 'todo', title: 'To Do', theme: 'primary', cards: [{ id: '1', title: 'Draft spec' }] },
  { id: 'done', title: 'Done', theme: 'success', cards: [] },
])

function onMove(payload) {
  console.log(payload.card, payload.from, '->', payload.to, payload.newIndex)
}
</script>

Other wrappers

The plugins entry also exports these wrappers, which follow the same dynamic-import / <ClientOnly> pattern:

ComponentPeer library
LteSparklineChartchart.js
LteEditorquill
LteTomSelecttom-select
LteFlatpickrflatpickr
LteVectorMapjsvectormap