Overview
In this tutorial, we will configure the Vue Scheduler component to accept tasks dragged from an ordered queue of unscheduled tasks.
The Queue component displays a list of unscheduled tasks. Users can change the task position or priority using drag and drop.
Tasks that have already been scheduled can be moved back to the queue using drag and drop.
You can download the attached Vue 3 project with TypeScript source code.
License
Licensed for testing and evaluation purposes. Please see LICENSE.md in the sample project. You can use the source code of the tutorial if you are a licensed user of DayPilot Pro for JavaScript.
How to configure the Vue Scheduler for external drag and drop?

In the first step, we will add the Vue Scheduler component from DayPilot Pro for JavaScript to our application. If you want to start with the basics, see Vue Scheduler: Build a Reservation Application in 5 Minutes. The current sample keeps the structure generated by the UI Builder, updates it to the latest Vue and DayPilot package versions, and adds the queue-specific logic in src/components/Scheduler.vue.
The Scheduler itself still uses the standard builder pattern: a reactive config object, a component ref for imperative calls, and current-month sample data loaded during onMounted().
How to remove the tasks from the queue when they are dropped in the Scheduler?
Extend the onEventMoved event handler to cover external drag and drop. When a task is dragged from the queue, args.external is true, so we remove the original item from the Queue using events.remove().
function schedulerOnEventMoved(args: DayPilot.SchedulerEventMovedArgs): void {
if (args.external) {
queueRef.value?.control.events.remove(args.e.data.id);
}
}How to enable dragging the tasks back to the queue?
By default, tasks can only be moved to another location inside the Scheduler grid. To enable dragging tasks outside of the Scheduler, add dragOutAllowed: true to the configuration. The dragOutAllowed property lets users drag already scheduled tasks back to the queue. The Queue component accepts the external item automatically.
What is needed to add the Vue Scheduler component?
The template keeps the Scheduler in the right-side panel:
<div class="scheduler-panel">
<DayPilotScheduler :config="config" ref="schedulerRef" />The important scheduler settings live in the reactive config object. A small startDate helper points to the first day of the current month, and the configuration keeps drag-and-drop event moving enabled while storing resources and events directly on the config object.
const config = reactive<DayPilot.SchedulerConfig>({
eventClickHandling: 'Disabled',
eventMoveHandling: 'Update',
onEventMoved: schedulerOnEventMoved,
timeRangeSelectedHandling: 'Enabled',
onTimeRangeSelected: schedulerOnTimeRangeSelected,
onBeforeEventRender: onBeforeEventRender,
dragOutAllowed: true,
treeEnabled: true,
rowHeaderWidth: 120,
cellWidth: 60,
eventHeight,
eventBorderRadius: 8,
startDate,
days: startDate.daysInMonth(),
timeHeaders: [{ groupBy: 'Month' }, { groupBy: 'Day', format: 'd' }],
scale: 'Day',
resources: [],
events: []
});The same onBeforeEventRender callback is shared by the Scheduler and the Queue. It hides the default duration bar, applies softer colors, and adds a three-dots context-menu button using the bundled SVG icon sprite in public/icons/daypilot.svg.
function onBeforeEventRender(args: any): void {
args.data.barHidden = true;
args.data.backColor = '#eef6f0';
args.data.borderColor = '#7ea58a';
args.data.fontColor = '#20362a';
args.data.borderRadius = 8;
args.data.areas = [
{
top: 0,
bottom: 0,
right: menuButtonInset,
width: menuButtonSize,
height: menuButtonSize,
padding: 2,
symbol: 'icons/daypilot.svg#threedots-v',
backColor: '#5c8d68',
fontColor: '#ffffff',
style: 'border-radius: 999px; cursor: pointer; margin: auto 0;',
action: 'ContextMenu',
menu: taskMenu
}
];
}How to add a Queue of unscheduled tasks?

The queue is implemented using the <DayPilotQueue> component. It provides a convenient way of handling an ordered queue of tasks:
The task data format is shared with the Scheduler.
The same event-rendering callback can style both scheduled and unscheduled tasks.
You can reorder tasks using drag and drop.
Queue items are automatically activated as external items that can be moved to the Scheduler.
Tasks can be moved from the Scheduler back to the queue.
Instead of using the Vue Queue component, it is also possible to implement your own queue using DayPilot.Scheduler.makeDraggable() and DayPilot.Scheduler.registerDropTarget().
How to add the Queue component?
The queue lives in the left-side panel next to the Scheduler:
<div class="layout">
<div class="queue-panel">
<button @click="addClick" class="button-add">New Task...</button>
<DayPilotQueue :config="queueConfig" ref="queueRef" />The Queue configuration stays small. It uses its own reactive queueConfig object, the same event-rendering callback as the Scheduler, and a separate queueRef for direct API access when needed. You can add more properties and event handlers here as needed.
const queueConfig = reactive<DayPilot.QueueConfig>({
onEventMoved: queueOnEventMoved,
onBeforeEventRender: onBeforeEventRender,
eventHeight,
events: []
});How to load the queue items?
The refreshed sample loads the unscheduled tasks by assigning them to the reactive queue configuration object:
function loadQueueEvents(): void {
queueConfig.events = [
{ id: 'Q1', text: 'Incoming request', duration: DayPilot.Duration.ofDays(2) },
{ id: 'Q2', text: 'Site visit', duration: DayPilot.Duration.ofDays(1) },
{ id: 'Q3', text: 'Replace controller', duration: DayPilot.Duration.ofDays(3) },
{ id: 'Q4', text: 'Maintenance window', duration: DayPilot.Duration.ofDays(4) }
];
}Each queue item stores a duration instead of a fixed start and end. That makes it easy to drag the task to the Scheduler later and pick the exact target slot there. If you prefer imperative updates, the Queue still exposes the update() method through queueRef.value?.control.
How to handle external drag and drop?
The Queue component lets users drag tasks to a different position in the queue and also accepts items dragged from the Scheduler. To detect the source of the task, check the args.external value in the onEventMoved handler.
Tasks moved from the Scheduler have
args.externalset totrue.The target position is stored as a zero-based index in
args.position.
function queueOnEventMoved(args: any): void {
if (args.external) {
schedulerRef.value?.control.events.remove(args.e.data.id);
}
console.log('target position', args.position);
}How to add a "New Task" button

The button above the queue opens a modal dialog, collects the task text, and appends a new unscheduled item to the bottom of the queue. The sample gives new queue items a default duration of two days so they can be scheduled immediately after creation.
const addClick = async () => {
const queue = queueRef.value?.control;
if (!queue) {
return;
}
const form = [
{ name: 'Task name', id: 'text', type: 'text' }
];
const data = {
text: 'New task'
};
const modal = await DayPilot.Modal.form(form, data);
if (modal.canceled) {
return;
}
queue.events.add({
id: DayPilot.guid(),
text: modal.result.text,
duration: DayPilot.Duration.ofDays(2)
});
};The addClick() function uses events.add() to append the new queue item without rebuilding the whole task list.
Full Source Code
Here is the full source code of src/components/Scheduler.vue, which combines the Scheduler component, the queue of unscheduled tasks, the shared event styling, and the queue/scheduler drag-and-drop behavior.
<template>
<div class="layout">
<div class="queue-panel">
<button @click="addClick" class="button-add">New Task...</button>
<DayPilotQueue :config="queueConfig" ref="queueRef" />
</div>
<div class="scheduler-panel">
<DayPilotScheduler :config="config" ref="schedulerRef" />
</div>
</div>
</template>
<script setup lang="ts">
import { DayPilot, DayPilotQueue, DayPilotScheduler } from 'daypilot-pro-vue';
import { onMounted, reactive, ref } from 'vue';
const startDate = DayPilot.Date.today().firstDayOfMonth();
const day = (offset: number) => startDate.addDays(offset);
const eventHeight = 44;
const menuButtonSize = 20;
const menuButtonInset = 8;
const schedulerRef = ref<DayPilotScheduler | null>(null);
const queueRef = ref<DayPilotQueue | null>(null);
function removeTask(id: string | number): void {
schedulerRef.value?.control.events.remove(id);
queueRef.value?.control.events.remove(id);
}
const taskMenu = new DayPilot.Menu({
items: [
{
text: 'Delete',
onClick: (args: any) => {
removeTask(args.source.data.id);
}
}
]
});
async function schedulerOnTimeRangeSelected(args: DayPilot.SchedulerTimeRangeSelectedArgs): Promise<void> {
const scheduler = args.control;
const form = [
{ name: 'Task name', id: 'text', type: 'text' }
];
const data = {
text: 'New task'
};
const modal = await DayPilot.Modal.form(form, data);
scheduler.clearSelection();
if (modal.canceled) {
return;
}
scheduler.events.add({
start: args.start,
end: args.end,
id: DayPilot.guid(),
resource: args.resource,
text: modal.result.text
});
}
function schedulerOnEventMoved(args: DayPilot.SchedulerEventMovedArgs): void {
if (args.external) {
queueRef.value?.control.events.remove(args.e.data.id);
}
}
function queueOnEventMoved(args: any): void {
if (args.external) {
schedulerRef.value?.control.events.remove(args.e.data.id);
}
console.log('target position', args.position);
}
function onBeforeEventRender(args: any): void {
args.data.barHidden = true;
args.data.backColor = '#eef6f0';
args.data.borderColor = '#7ea58a';
args.data.fontColor = '#20362a';
args.data.borderRadius = 8;
args.data.areas = [
{
top: 0,
bottom: 0,
right: menuButtonInset,
width: menuButtonSize,
height: menuButtonSize,
padding: 2,
symbol: 'icons/daypilot.svg#threedots-v',
backColor: '#5c8d68',
fontColor: '#ffffff',
style: 'border-radius: 999px; cursor: pointer; margin: auto 0;',
action: 'ContextMenu',
menu: taskMenu
}
];
}
const config = reactive<DayPilot.SchedulerConfig>({
eventClickHandling: 'Disabled',
eventMoveHandling: 'Update',
onEventMoved: schedulerOnEventMoved,
timeRangeSelectedHandling: 'Enabled',
onTimeRangeSelected: schedulerOnTimeRangeSelected,
onBeforeEventRender: onBeforeEventRender,
dragOutAllowed: true,
treeEnabled: true,
rowHeaderWidth: 120,
cellWidth: 60,
eventHeight,
eventBorderRadius: 8,
startDate,
days: startDate.daysInMonth(),
timeHeaders: [{ groupBy: 'Month' }, { groupBy: 'Day', format: 'd' }],
scale: 'Day',
resources: [],
events: []
});
const queueConfig = reactive<DayPilot.QueueConfig>({
onEventMoved: queueOnEventMoved,
onBeforeEventRender: onBeforeEventRender,
eventHeight,
events: []
});
function loadEvents(): void {
config.events = [
{ id: 'S1', start: day(19), end: day(21), text: 'Inspection', resource: 'R2' },
{ id: 'S2', start: day(13), end: day(16), text: 'Preparation', resource: 'R4' },
{ id: 'S3', start: day(14), end: day(18), text: 'Installation', resource: 'R1' },
{ id: 'S4', start: day(17), end: day(20), text: 'Testing', resource: 'R6' }
];
}
function loadResources(): void {
config.resources = [
{ name: 'Resource 1', id: 'R1' },
{ name: 'Resource 2', id: 'R2' },
{ name: 'Resource 3', id: 'R3' },
{ name: 'Resource 4', id: 'R4' },
{ name: 'Resource 5', id: 'R5' },
{ name: 'Resource 6', id: 'R6' },
{ name: 'Resource 7', id: 'R7' }
];
}
function loadQueueEvents(): void {
queueConfig.events = [
{ id: 'Q1', text: 'Incoming request', duration: DayPilot.Duration.ofDays(2) },
{ id: 'Q2', text: 'Site visit', duration: DayPilot.Duration.ofDays(1) },
{ id: 'Q3', text: 'Replace controller', duration: DayPilot.Duration.ofDays(3) },
{ id: 'Q4', text: 'Maintenance window', duration: DayPilot.Duration.ofDays(4) }
];
}
const addClick = async () => {
const queue = queueRef.value?.control;
if (!queue) {
return;
}
const form = [
{ name: 'Task name', id: 'text', type: 'text' }
];
const data = {
text: 'New task'
};
const modal = await DayPilot.Modal.form(form, data);
if (modal.canceled) {
return;
}
queue.events.add({
id: DayPilot.guid(),
text: modal.result.text,
duration: DayPilot.Duration.ofDays(2)
});
};
onMounted(() => {
loadResources();
loadEvents();
loadQueueEvents();
schedulerRef.value?.control.scrollTo(DayPilot.Date.today());
schedulerRef.value?.control.message('Drag tasks from the queue to schedule them.');
});
</script>
<style>
body .scheduler_default_event_inner,
body .queue_default_event_inner {
background: #eef6f0;
color: #20362a;
border: 1px solid #7ea58a;
border-radius: 8px;
padding: 6px 34px 6px 10px;
margin: 2px;
box-sizing: border-box;
}
</style>
<style scoped>
.layout {
display: flex;
align-items: flex-start;
gap: 12px;
}
.queue-panel {
width: 230px;
flex: 0 0 230px;
}
.scheduler-panel {
flex: 1 1 auto;
min-width: 0;
}
.button-add {
width: 100%;
margin-bottom: 10px;
padding: 10px 12px;
background-color: #f3f6f4;
border: 1px solid #c8d4cc;
border-radius: 8px;
color: #20362a;
cursor: pointer;
}
.button-add:hover {
background-color: #e9efeb;
}
</style>
History
April 18, 2026: Upgraded to Vue 3.5, Vite 7, and DayPilot Pro for JavaScript 2026.2. Rebuilt the sample on the current Vue Scheduler builder template, switched to current-month demo data, and refreshed the task styling/menu icon setup.
June 14, 2024: Upgraded to Vue 3, Composition API, DayPilot Pro 2024.2.5951. Additional task styling.
July 16, 2021: Initial release.
DayPilot




