Using Preline UI with Nuxt
A practical guide to using Preline UI JavaScript plugins in Nuxt projects, including client plugins, page:finish, module imports, cleanup, and optional dependencies.
View guideUpdate v4.2 - New components, 10+ framework guides, and quality improvements. Visit Changelog
A practical guide to wiring Preline UI into Vue without relying on stale page-load examples.
Vue and Preline UI work best together when Vue owns rendering and Preline UI owns the DOM behavior that starts after rendering. The important detail is timing: Vue updates the DOM asynchronously, so Preline UI should initialize after Vue has finished rendering the elements it needs to scan.
The current Preline UI package exposes preline/non-auto, named plugin classes, collection-aware autoInit, and single-plugin packages. Those pieces map cleanly to Vue lifecycle hooks because Vue can render first and Preline UI can scan only the DOM that already exists.
Preline UI is not a Vue component library. It is a DOM-driven Tailwind CSS component system. Vue renders the markup, then Preline UI attaches behavior to nodes such as dropdowns, overlays, selects, tabs, tooltips, and range sliders.
This is why the integration should be explicit. You do not need Preline UI to manage Vue state, and you do not need to wrap every plugin in a custom Vue abstraction. You only need to initialize after the relevant DOM exists, and clean up when Vue removes manually created instances.
Vue already gives you the signal Preline UI needs: nextTick. Wait for Vue to flush the DOM update, then ask Preline UI to scan the rendered markup.
<script setup lang="ts">
import { nextTick, onMounted } from "vue";
import { HSStaticMethods } from "preline/non-auto";
onMounted(async () => {
await nextTick();
HSStaticMethods.autoInit();
});
</script>
Static browser scripts and single-package auto entries can initialize on page load. In Vue, preline/non-auto gives you the same static helpers without making page-load timing the source of truth.
Route navigation is the common case where Preline UI needs another pass. A route can replace a large part of the page, so run autoInit after navigation has finished and Vue has rendered the target view.
import { nextTick } from "vue";
import { createRouter, createWebHistory } from "vue-router";
import { HSStaticMethods } from "preline/non-auto";
const router = createRouter({
history: createWebHistory(),
routes: [
...
],
});
router.afterEach(async (_to, _from, failure) => {
if (failure) return;
await nextTick();
HSStaticMethods.autoInit();
});
export default router;
If you only use a few plugins, pass the collection keys explicitly. The static methods map includes keys such as dropdown, overlay, select, tooltip, tabs, and range-slider.
await nextTick();
HSStaticMethods.autoInit(["dropdown", "overlay"]);
For most Vue apps, the best full-library import is preline/non-auto. It provides HSStaticMethods and plugin classes without depending on browser script or plugin auto-entry page-load timing.
import { createApp } from "vue";
import App from "./App.vue";
import { HSStaticMethods } from "preline/non-auto";
const app = createApp(App);
app.provide("preline", HSStaticMethods);
app.mount("#app");
If a view only needs one plugin class from the full package, import that class from preline/non-auto. It keeps strict TypeScript builds on the declared package surface.
import { HSDropdown } from "preline/non-auto";
HSDropdown.autoInit();
Preline UI plugins can also be consumed from single-plugin dependencies when those packages are available in your dependency set, for example @preline/dropdown, @preline/overlay, @preline/select, or @preline/range-slider. This is useful when a Vue surface only needs one or two interactive primitives.
npm install @preline/dropdown
<script setup lang="ts">
import { nextTick, onMounted } from "vue";
import HSDropdown from "@preline/dropdown/non-auto";
onMounted(async () => {
await nextTick();
HSDropdown.autoInit();
});
</script>
In that single-package setup, the auto entry is import "@preline/dropdown". Use it for simple static pages. In Vue components and routed apps, /non-auto is easier to reason about.
autoInit is convenient for page-level scans. Manual instances are better when a Vue component owns one plugin root and can control its lifecycle directly. This keeps cleanup local and avoids leaving stale plugin references when the component unmounts.
<script setup lang="ts">
import { nextTick, onBeforeUnmount, onMounted, ref } from "vue";
import { HSDropdown, type IHTMLElementFloatingUI } from "preline/non-auto";
const dropdownRef = ref<HTMLElement | null>(null);
let dropdown: HSDropdown | null = null;
onMounted(async () => {
await nextTick();
if (dropdownRef.value) {
dropdown = new HSDropdown(dropdownRef.value as unknown as IHTMLElementFloatingUI);
}
});
onBeforeUnmount(() => {
dropdown?.destroy();
dropdown = null;
});
</script>
<template>
<div ref="dropdownRef" class="hs-dropdown relative inline-flex">
...
</div>
</template>
Preline UI stores plugin instances in internal collections such as window.$hsDropdownCollection. This is how plugins coordinate in plain HTML, Vue, React, Nuxt, Astro, and other environments without relying on a framework-specific context.
Vue developers should treat that registry like any other DOM integration. If you create an instance manually, call destroy() in onBeforeUnmount. If you initialize a whole route and intentionally remove that route's markup, clean the related collections deliberately.
HSStaticMethods.cleanCollection("dropdown");
HSStaticMethods.cleanCollection(["dropdown", "overlay"]);
The current plugin code guards optional integrations. Install third-party libraries only for the plugins your Vue app actually initializes.
Most core plugins do not use jQuery. Dropdowns, overlays, tooltips, popovers, tabs, and similar components use plain JavaScript. Positioning behavior uses @floating-ui/dom. jQuery is only relevant for Datatable because datatables.net depends on it.
Range Slider uses the JavaScript API from noUiSlider. Preline UI remains responsible for the Tailwind CSS markup and behavior wrapper; noUiSlider CSS classes do not need to be merged into Preline UI styling.
preline/non-auto in Vue when you want explicit lifecycle control.HSStaticMethods.autoInit() after nextTick(), not before Vue has rendered the target markup.router.afterEach plus nextTick for routed apps.ref, onMounted, and onBeforeUnmount for reusable Vue components.datatables.net for Datatable or noUiSlider for Range Slider.A practical guide to using Preline UI JavaScript plugins in Nuxt projects, including client plugins, page:finish, module imports, cleanup, and optional dependencies.
View guideA practical guide to using Preline UI JavaScript plugins in Svelte and SvelteKit projects, including onMount, tick, afterNavigate, actions, cleanup, and optional dependencies.
View guide