Vue Custom Directive for jQuery Datepicker
Integrating legacy jQuery UI plugins into modern Vue.js applications can be accomplished cleanly without polluting component logic by using custom directives. This guide demonstrates how to create a reusable custom Vue directive that initializes a jQuery datepicker, synchronizes date selections with Vue's reactivity system, and properly destroys the plugin instance to prevent memory leaks.
Prerequisites
Ensure jQuery and jQuery UI are installed and imported into your project:
npm install jquery jquery-ui-dist1. Creating the Custom Directive
In Vue 3, custom directives provide lifecycle hooks such as
mounted and beforeUnmount. Create a new file
named vDatepicker.js:
import $ from 'jquery';
import 'jquery-ui-dist/jquery-ui';
import 'jquery-ui-dist/jquery-ui.css';
export default {
mounted(el, binding) {
// Initialize the jQuery datepicker on the element
$(el).datepicker({
dateFormat: 'yy-mm-dd',
// Pass any options passed as the directive value
...binding.value,
onSelect: function (dateText) {
// Dispatch native input and change events so v-model updates
el.value = dateText;
el.dispatchEvent(new Event('input', { bubbles: true }));
el.dispatchEvent(new Event('change', { bubbles: true }));
}
});
},
beforeUnmount(el) {
// Clean up the jQuery UI instance to avoid memory leaks
if ($(el).data('datepicker')) {
$(el).datepicker('destroy');
}
}
};2. Registering the Directive
You can register the directive globally in your main.js
file:
import { createApp } from 'vue';
import App from './App.vue';
import vDatepicker from './directives/vDatepicker';
const app = createApp(App);
app.directive('datepicker', vDatepicker);
app.mount('#app');Alternatively, register it locally within a single component:
import vDatepicker from '@/directives/vDatepicker';
export default {
directives: {
datepicker: vDatepicker
}
};3. Using the Directive in a Component
Apply the directive to a standard <input> element
alongside v-model. You can also pass jQuery UI
configuration options directly into the directive expression:
<template>
<div>
<label for="birthdate">Select Date:</label>
<input
id="birthdate"
type="text"
v-datepicker="{ minDate: 0, maxDate: '+1M' }"
v-model="selectedDate"
placeholder="Select a date"
/>
<p>Selected Date: {{ selectedDate }}</p>
</div>
</template>
<script setup>
import { ref } from 'vue';
const selectedDate = ref('');
</script>
Key Considerations
- Two-Way Binding: Because jQuery modifies the DOM
directly, the standard Vue
v-modellistener does not detect the change automatically. Manually triggering theinputevent insideonSelectsolves this problem. - Component Cleanup: Always destroy the jQuery plugin
inside
beforeUnmount(orunbindin Vue 2) to free references and avoid lingering event listeners when the element leaves the DOM.