Charts & plugins
Heavy third-party libraries are wrapped in thin components that lazy-load their dependency only on the browser, only when the component is used. Install the peers you need; skip the ones you don't.
How lazy loading works
The plugin wrappers (LteApexChart, LteDatatable, LteInputFlatpickr, LteInputTomSelect) declare their library as an optional peer dependency and import it with a dynamic import() guarded by a platform check. So the library never bloats your main bundle, never runs on the server during SSR/prerender, and degrades gracefully (a static table, a plain input) if the peer isn't installed — logging a single console warning. Install only what you use:
npm install apexcharts flatpickr tom-select simple-datatables
ApexCharts
<lte-apex-chart> takes a raw ApexCharts options object via its required options input. Pass any valid ApexCharts config (series, chart, xaxis, …); when the input changes, the chart re-renders, and it is destroyed on teardown.
import { Component, computed, signal } from '@angular/core';
import { AppContentComponent, CardComponent, ApexChartComponent } from '@adminlte/angular';
@Component({
selector: 'app-dashboard',
imports: [AppContentComponent, CardComponent, ApexChartComponent],
template: `
<lte-app-content title="Dashboard">
<lte-card title="Sales">
<lte-apex-chart [options]="chartOptions()" />
</lte-card>
</lte-app-content>
`,
})
export class DashboardPage {
private readonly series = signal([30, 40, 45, 50, 49, 60, 70]);
readonly chartOptions = computed(() => ({
chart: { type: 'area', height: 300, toolbar: { show: false } },
series: [{ name: 'Revenue', data: this.series() }],
xaxis: { categories: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] },
}));
}
Build options as a computed() so the chart reacts to your signals automatically. apexcharts is an optional peer — install it to activate the wrapper.
Datatables
<lte-datatable> renders a plain Bootstrap table from columns + data, then progressively enhances it with simple-datatables (search, sort, paginate). Columns are DatatableColumn — { key, label?, sortable? }.
<lte-datatable
[columns]="[
{ key: 'name', label: 'Name' },
{ key: 'role', label: 'Role' },
{ key: 'joined', label: 'Joined', sortable: false }
]"
[data]="rows"
[options]="{ perPage: 10 }"
/>
Flatpickr & Tom Select
Two form controls wrap popular pickers. Both are ControlValueAccessors with a [(value)] model, and accept extra library settings.
<lte-input-flatpickr
label="Event date"
[options]="{ dateFormat: 'Y-m-d' }"
[(value)]="date"
/>
<lte-input-tom-select
label="Tags"
[multiple]="true"
[options]="[{ value: 'ng', text: 'Angular' }, { value: 'ts', text: 'TypeScript' }]"
[(value)]="tags"
/>
<lte-input-flatpickr> forwards extra flatpickr options through [options]; <lte-input-tom-select> forwards Tom Select settings through [settings] and takes its choices as TomSelectOption[].
Plugin-heavy demo pages
Some demo pages use libraries that have no first-class wrapper (FullCalendar, Quill, SortableJS, jsVectorMap). The pattern there is to load the library inside Angular's afterNextRender using dynamic imports, so SSR/prerender never touches the browser-only code, and to wrap the whole init in try/catch so a failed load degrades to an empty card instead of breaking the page:
import { Component, ElementRef, afterNextRender, viewChild } from '@angular/core';
@Component({ /* ... */ })
export class CalendarPage {
private readonly calendarEl = viewChild.required<ElementRef>('calendar');
constructor() {
afterNextRender(async () => {
try {
const { Calendar } = await import('@fullcalendar/core');
const dayGrid = (await import('@fullcalendar/daygrid')).default;
const calendar = new Calendar(this.calendarEl().nativeElement, {
plugins: [dayGrid],
initialView: 'dayGridMonth',
});
calendar.render();
} catch {
// optional progressive enhancement — leave the card empty on failure
}
});
}
}
The calendar, kanban (SortableJS) and editor (Quill) demo pages all follow this afterNextRender + dynamic-import recipe. Use it whenever you integrate a browser-only library that the library doesn't wrap for you.