Most core plugins do not use a third-party library at all. Dropdowns, overlays, tooltips, popovers, tabs, and similar components use plain JavaScript. Positioning behavior uses @floating-ui/dom. A handful of plugins wrap a third-party library instead, and each one checks for its own dependency on window before it will initialize:
- Datatable needs
window.jQuery and window.DataTable, because datatables.net depends on jQuery.
- Datepicker needs
window.VanillaCalendarPro and window._ (lodash). The lodash dependency is easy to miss since it is not obvious from the plugin's name, but its internal options-merging calls _.merge/_.mergeWith directly.
- Range Slider needs
window.noUiSlider. Preline UI remains responsible for the Tailwind CSS markup and behavior wrapper, so you do not need to bring in noUiSlider's own CSS just to make Preline UI styling work.
- File Upload needs
window.Dropzone and window._ (lodash) together.
If a required global is missing, that plugin simply does not initialize; it does not affect dropdowns, overlays, tabs, tooltips, or any other plugin.
Under the esbuild auto entry, set each window global from the corresponding npm package before importing preline. This is one place where import order genuinely matters and is easy to get subtly wrong: a static import placed above import "preline" looks like it runs first, but ES module imports are hoisted, so every statically imported module, including preline itself, finishes evaluating before any of the file's own top-level statements run. The auto entry decides once, at that evaluation, whether each dependency-gated plugin is available, so a same-file assignment like window.DataTable = DataTable is too late even though it appears earlier in the source. Dynamic import() calls do not have this problem, since each one is a real asynchronous step that runs when your code reaches it, not when the module graph loads:
async function initPreline() {
const { default: $ } = await import("jquery");
window.$ = $;
window.jQuery = $;
const { default: DataTable } = await import("datatables.net");
window.DataTable = DataTable;
const { default: _ } = await import("lodash");
window._ = _;
const { Calendar } = await import("vanilla-calendar-pro");
window.VanillaCalendarPro = Calendar;
const { default: noUiSlider } = await import("nouislider");
window.noUiSlider = noUiSlider;
const { default: Dropzone } = await import("dropzone");
window.Dropzone = Dropzone;
await import("preline");
window.HSStaticMethods?.autoInit();
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", initPreline);
} else {
initPreline();
}
Only import the packages the page actually uses; the list above shows all five together for illustration. This initPreline function replaces the plain import "preline" shown in the PrelineInit hook setup above as soon as the page needs one of these plugins; the PrelineInit hook itself does not change, it just ends up calling autoInit after this async setup has run instead of after a synchronous import. Under Vite, this ordering concern does not apply the same way, since preline/non-auto exposes plugin classes without an eager availability check at import time, so set the relevant window global before you call that plugin's own autoInit().