Full-Featured Drag-and-Drop JavaScript Event Calendar with Resource & Timeline Views
The calendar works as a standalone module with zero dependencies. You can use it with JavaScript modules, Svelte components, or a standalone browser bundle.
1. Install and import EventCalendar with NPM. The npm package requires at least one view plugin. The available view plugins are: DayGrid, List, ResourceTimeline, ResourceTimeGrid, TimeGrid, and Interaction. The Interaction plugin adds drag-and-drop and date selection but does not provide a view on its own. Import the plugins you need alongside createCalendar.
# NPM $ npm install @event-calendar/core
import { createCalendar, destroyCalendar, TimeGrid } from '@event-calendar/core';
// Import the stylesheet if your build tool supports CSS imports
import '@event-calendar/core/index.css'; 2. You can also include the standalone bundle directly in your HTML document.
<link rel="stylesheet" href="/dist/event-calendar.min.css"> <script src="/dist/event-calendar.min.js"></script> <!-- Or from a CDN --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@event-calendar/build@latest/dist/event-calendar.min.css"> <script src="https://cdn.jsdelivr.net/npm/@event-calendar/build@latest/dist/event-calendar.min.js"></script>
3. Create a basic event calendar.
<div id="team-schedule"></div>
// Mount the calendar to a DOM element with id="team-schedule"
let ec = createCalendar(
document.getElementById('team-schedule'),
// Pass an array of view plugins — at least one is required
[TimeGrid],
{
view: 'timeGridWeek', // Start in weekly time grid view
firstDay: 1, // 1 = Monday as the first day of the week
scrollTime: '08:00:00', // Scroll the time axis to 8 AM on initial render
nowIndicator: true, // Show a line marking the current time
events: [
{
id: 'standup-1',
title: 'Team Standup',
start: '2026-04-07 09:00:00',
end: '2026-04-07 09:30:00'
},
{
id: 'review-2',
title: 'Design Review',
start: '2026-04-08 14:00:00',
end: '2026-04-08 16:00:00',
backgroundColor: '#2c7be5', // Per-event background color override
textColor: '#ffffff'
}
]
}
);
// Destroy the calendar instance when you no longer need it
destroyCalendar(ec); // Standalone Bundle Setup
let ec = EventCalendar.create(document.getElementById('team-schedule'), {
// ...
} 4. Drag-and-Drop with Resource Views:
import { createCalendar, ResourceTimeGrid, Interaction } from '@event-calendar/core';
import '@event-calendar/core/index.css';
let ec = createCalendar(
document.getElementById('staff-schedule'),
// ResourceTimeGrid provides the view; Interaction adds drag-and-drop
[ResourceTimeGrid, Interaction],
{
view: 'resourceTimeGridDay',
editable: true, // Enables drag-and-drop and resize for all events
resources: [
{ id: 'r-alice', title: 'Alice Chen' },
{ id: 'r-bob', title: 'Bob Martinez' },
{ id: 'r-carol', title: 'Carol Patel' }
],
events: [
{
id: 'appt-101',
resourceId: 'r-alice', // Associates this event with Alice's column
title: '1:1 Performance Review',
start: '2026-04-07 10:00:00',
end: '2026-04-07 10:45:00'
},
{
id: 'appt-102',
resourceId: 'r-bob',
title: 'Client Onboarding Call',
start: '2026-04-07 11:00:00',
end: '2026-04-07 12:30:00',
backgroundColor: '#e67e22'
},
{
// resourceIds (plural) assigns one event to multiple resources
id: 'appt-103',
resourceIds: ['r-alice', 'r-carol'],
title: 'Shared Workshop',
start: '2026-04-07 14:00:00',
end: '2026-04-07 15:30:00'
}
],
eventDrop: function(info) {
// Fires after a successful drop at a new date/time or resource
// info.event reflects the updated state; info.oldEvent holds the previous state
// Call info.revert() to cancel the move programmatically
console.log(info.event.title + ' dropped to', info.event.start);
if (info.newResource) {
console.log('New resource:', info.newResource.title);
}
},
eventResize: function(info) {
// Fires after the user extends or shortens an event by dragging its edge
console.log('Updated end time:', info.event.end);
// Call info.revert() to cancel the resize
},
dragConstraint: function(info) {
// Return false to block a drop into specific time slots
// info contains the same properties as eventDrop
let hour = info.event.start.getHours();
return hour >= 8 && hour < 18; // Restrict drops to business hours only
}
}
); 5. Fetching Events from a URL:
let ec = createCalendar(
document.getElementById('team-schedule'),
[TimeGrid],
{
view: 'timeGridWeek',
// eventSources replaces the events array when remote data is needed
eventSources: [
{
url: '/api/calendar/events', // EventCalendar appends ?start=...&end=...
method: 'GET',
extraParams: {
// Pass any additional query parameters here
projectId: 101,
timezone: 'America/New_York'
}
}
],
loading: function(isLoading) {
// isLoading is true when fetching begins, false when it completes
document.getElementById('calendar-spinner').style.display =
isLoading ? 'flex' : 'none';
}
}
);
// Manually trigger a refetch when your data changes on the server
ec.refetchEvents(); 6. You can also pass an async function as the event source if you need custom fetch logic:
eventSources: [
{
events: async function(fetchInfo, successCallback, failureCallback) {
// fetchInfo.start and fetchInfo.end are Date objects for the visible range
try {
const res = await fetch(
`/api/events?from=${fetchInfo.startStr}&to=${fetchInfo.endStr}`
);
const data = await res.json();
successCallback(data); // Pass the event array to the calendar
} catch (err) {
failureCallback(err); // Signal the failure to the calendar
}
}
}
] 7. Modifying Options at Runtime:
// Read the current value of any option
let activeView = ec.getOption('view');
let currentDate = ec.getOption('date');
// Update any option — the calendar re-renders to reflect the change immediately
ec.setOption('view', 'dayGridMonth');
ec.setOption('date', new Date('2026-07-01'));
ec.setOption('slotDuration', '00:15:00'); // Switch from 30-min to 15-min time slots
// setOption returns the calendar instance for chaining
ec.setOption('firstDay', 1).setOption('slotHeight', 32); 8. All configuration options:
Display and Layout
view (string): The active calendar view. Options: 'dayGridDay', 'dayGridWeek', 'dayGridMonth', 'listDay', 'listWeek', 'listMonth', 'listYear', 'resourceTimeGridDay', 'resourceTimeGridWeek', 'resourceTimelineDay', 'resourceTimelineWeek', 'resourceTimelineMonth', 'resourceTimelineYear', 'timeGridDay', 'timeGridWeek'. Default: 'timeGridWeek'.views (object): Per-view option overrides and custom view definitions. Use the type property to inherit from a standard view.date (Date | string): The date currently displayed on the calendar. Accepts a JavaScript Date object or an ISO8601 string. Default: new Date().duration (string | integer | object): The duration of a view, parsed as a Duration object. Default varies by view.height (string): The height of the entire calendar as a CSS value, e.g. '600px' or '100%'.headerToolbar (object): Toolbar layout defined with start, center, and end string properties. Values separated by commas render adjacently; values separated by spaces render with a small gap. Default: {start: 'title', center: '', end: 'today prev,next'}.buttonText (object | function): Text labels for toolbar navigation buttons.customButtons (object | function): Custom button definitions for placement in the header toolbar. Each entry takes text, click, and active properties.customScrollbars (boolean): Enables styled scrollbars in supported browsers. Default: false.columnWidth (string): Column width in timeGrid and resourceTimeGrid views as a CSS length value.slotDuration (string | integer | object): Frequency of displayed time slots, parsed as a Duration object. Default: '00:30:00'.slotHeight (integer): Height in pixels of each time slot row in timeGrid views. Default: 24.slotWidth (integer): Width in pixels of each time slot column in resourceTimeline views. Default: 32.slotMinTime (string | integer | object): The first time slot displayed for each day, parsed as a Duration object. Default: '00:00:00'.slotMaxTime (string | integer | object): The last time slot displayed for each day, parsed as a Duration object. Default: '24:00:00'.slotLabelFormat (object | function): Format of text labels inside time slots.slotLabelInterval (string | integer | object): Interval at which slot labels appear, parsed as a Duration object.slotEventOverlap (boolean): Controls whether intersecting events visually overlap in timeGrid views. Set to false to render them side-by-side. Default: true.scrollTime (string | integer | object): Initial scroll position in the time grid, parsed as a Duration object. Default: '06:00:00'.snapDuration (string | integer | object): Step size for dragging, resizing, and selection on the time axis. Defaults to slotDuration when not specified.nowIndicator (boolean): Displays a current-time marker line in timeGrid views. Default: false.firstDay (integer): First day of the week. Sunday = 0, Monday = 1, Saturday = 6. Default: 0.hiddenDays (array): Array of day-of-week integers to exclude from the calendar display.highlightedDates (array): An array of ISO8601 date strings or JavaScript Date objects to highlight on the calendar.weekNumbers (boolean): Shows week numbers in dayGrid and resourceTimeline views. Default: false.weekNumberContent (Content | function): Custom content for week number cells.dayMaxEvents (boolean): Limits stacked event levels per day in dayGrid views, showing a “+N more” link for overflow. Default: false.moreLinkContent (Content | function): Custom content for the “+N more” overflow link.dayPopoverFormat (object | function): Format of the popover title shown by the “+N more” link. Default: {month: 'long', day: 'numeric', year: 'numeric'}.validRange (object): Restricts calendar navigation to a date range using start and end properties. Either property is optional.locale (string): The locale string passed to Intl.DateTimeFormat for all date and time formatting.theme (object | function): CSS class name mappings for every calendar element.flexibleSlotTimeLimits (boolean | object): Automatically expands slotMinTime and slotMaxTime when events fall outside the visible time range. Default: false.pointer (boolean): Enables a pointer cursor style in applicable views. Requires Interaction plugin. Default: false.Duration Object Reference
Duration objects store time periods like 30 minutes or 3 days. Input values parse from three formats: an object with keys years, months, days, hours, minutes, or seconds; a string in hh:mm:ss or hh:mm format; or an integer representing total seconds.
Events
events (array): An array of plain event objects to display. Ignored when eventSources is set. Default: [].eventSources (EventSource[]): Event source definitions for URL fetching or custom async functions. Each source can include a url, method, and extraParams for HTTP fetching, or an events function for custom logic.lazyFetching (boolean): Fetches events only when the calendar needs data for a date range it has not yet cached. Default: true.eventBackgroundColor (string): Default background color for all calendar events.eventTextColor (string): Default text color for all calendar events.eventColor (string): Alias for eventBackgroundColor.eventClassNames (string | array | function): Additional CSS classes applied to event elements.eventContent (Content | function): Custom content rendered inside each event element.eventDidMount (function): Fires immediately after an event element is inserted into the DOM.eventTimeFormat (object | function): Format of the time text displayed on each event. Default: {hour: 'numeric', minute: '2-digit'}.displayEventEnd (boolean): Controls whether an event’s end time renders on the event. Default varies by view.eventOrder (function): Comparator function for ordering visually intersecting events. Return negative, zero, or positive values.eventFilter (function): Filter function applied to the event array before display. Return true to keep an event, false to hide it.eventAllUpdated (function): Fires after all events complete a full re-render cycle.When parsing events from plain objects, EventCalendar accepts the following fields:
id (string | integer): A unique identifier for the event. Auto-generated if omitted.title (Content): The text displayed on the event element.start (string | Date): Event start time as an ISO8601 string or Date object.end (string | Date): Event end time as an ISO8601 string or Date object.allDay (boolean): Forces the event into the all-day slot. Auto-detected from the start value when not specified.resourceId or resourceIds (string | integer | array): The resource ID or array of IDs this event belongs to.editable (boolean): Per-event override for the global editable setting.startEditable (boolean): Per-event override for drag permission.durationEditable (boolean): Per-event override for resize permission.display (string): Rendering mode. Use 'auto' for standard events or 'background' for background highlights.backgroundColor (string): Per-event background color as any CSS color value.textColor (string): Per-event text color as any CSS color value.color (string): Alias for backgroundColor.classNames or className (string | array): Additional CSS classes for this event.styles or style (string | array): Additional inline style declarations for this event.extendedProps (object): A plain object for any custom properties. Accessible at event.extendedProps in callbacks.Interaction
editable (boolean): Enables drag-and-drop and resize for all events. Requires Interaction plugin. Default: false.eventStartEditable (boolean): Controls whether events can be repositioned by dragging. Requires Interaction plugin. Default: true.eventDurationEditable (boolean): Controls whether events can be resized. Requires Interaction plugin. Default: true.eventResizableFromStart (boolean): Allows resizing an event by dragging its start edge. Requires Interaction plugin. Default: false.dragScroll (boolean): Auto-scrolls the calendar when the pointer nears the edge during a drag. Requires Interaction plugin. Default: true.eventDragMinDistance (integer): Pixel distance the pointer must travel before a drag begins. Requires Interaction plugin. Default: 5.longPressDelay (integer): Milliseconds a touch must be held before drag or selection activates. Default: 1000.eventLongPressDelay (integer): Overrides longPressDelay for event dragging on touch devices.selectable (boolean): Allows users to highlight date or time ranges by clicking and dragging. Requires Interaction plugin. Default: false.selectMinDistance (integer): Pixel distance the pointer must travel before a selection begins. Requires Interaction plugin. Default: 5.selectLongPressDelay (integer): Overrides longPressDelay for date selection on touch devices.selectBackgroundColor (string): Background color of the active selection highlight.unselectAuto (boolean): Clears the selection when the user clicks outside it. Requires Interaction plugin. Default: true.unselectCancel (string): CSS selector for elements that block automatic deselection.dragConstraint (function): Called on each pointer movement during a drag. Return false to block the drop at the current position.resizeConstraint (function): Called on each pointer movement during a resize. Return false to block the resize at the current size.selectConstraint (function): Called on each pointer movement during a selection. Return false to block the selection at the current range.Resources
resources (array | object | function): Resource data for resource views. Pass an array of plain objects, an object with a url property for HTTP fetching, or a custom function.datesAboveResources (boolean): Renders date headings above resource headings in resource views. Default: false.filterEventsWithResources (boolean): Hides events that do not belong to the current resource list in non-resource views. Default: false.filterResourcesWithEvents (boolean): Hides resources with no events in the current date range from resource views. Default: false.refetchResourcesOnNavigate (boolean): Re-fetches resources each time the user navigates to a new date. Default: false.resourceLabelContent (string | object | function): Custom content inside each resource label element.resourceLabelDidMount (function): Fires after a resource label element is inserted into the DOM.resourceExpand (function): Fires when a nested resource group is expanded or collapsed in resourceTimeline views.Resource Object Reference
id (integer | string): Unique resource identifier. Coerced to a string internally.title (Content): Text displayed in the resource column or row header.eventBackgroundColor (string): Default background color for all events belonging to this resource.eventTextColor (string): Default text color for all events belonging to this resource.expanded (boolean): Controls whether a resource with nested children renders expanded or collapsed. Default: true.extendedProps (object): Custom properties passed through to the Resource object.children (array): Nested resource definitions for hierarchical grouping in resourceTimeline views.Formatting
dayHeaderFormat (object | function): Format of column heading text. Default varies by view.dayHeaderAriaLabelFormat (object | function): Format of aria-label attribute text in column headings.dayCellFormat (object | function): Format of day number text inside cells in dayGridMonth view. Default: {day: 'numeric'}.titleFormat (object | function): Format of the header toolbar title text. Default varies by view.listDayFormat (object | function): Format of the left-side date heading in list views. Default: {weekday: 'long'}.listDaySideFormat (object | function): Format of the right-side date text in list views. Default: {year: 'numeric', month: 'long', day: 'numeric'}.monthHeaderFormat (object | function): Format of month headings in resourceTimelineYear view.allDayContent (Content | function): Content displayed in the all-day slot row heading.allDaySlot (boolean): Shows or hides the all-day slot row. Default: true.noEventsContent (Content | function): Content shown in list view when no events exist for the range. Default: 'No events'.icons (object | function): Icon definitions for expand and collapse buttons in resource timeline views.Callbacks
dateClick (function): Fires when the user clicks a date or time slot. Requires Interaction plugin.datesSet (function): Fires whenever the calendar’s displayed date range changes through navigation or view switching.eventClick (function): Fires when the user clicks an event element.eventMouseEnter (function): Fires when the pointer enters an event element.eventMouseLeave (function): Fires when the pointer leaves an event element.eventDragStart (function): Fires at the start of an event drag. Requires Interaction plugin.eventDragStop (function): Fires when an event drag ends, before position updates apply. Requires Interaction plugin.eventDrop (function): Fires after an event is dropped at a new date, time, or resource. Requires Interaction plugin.eventResizeStart (function): Fires at the start of an event resize. Requires Interaction plugin.eventResizeStop (function): Fires when a resize ends, before duration updates apply. Requires Interaction plugin.eventResize (function): Fires after an event’s duration changes through a completed resize. Requires Interaction plugin.select (function): Fires when a date or time range selection completes. Requires Interaction plugin.unselect (function): Fires when the current selection is cleared. Requires Interaction plugin.noEventsClick (function): Fires when the user clicks the “No events” area in list view.viewDidMount (function): Fires after a view is added to the DOM.loading (function): Fires when event or resource fetching starts or stops, passing a boolean isLoading argument.9. API methods.
// Read the current value of any calendar option
let currentDate = ec.getOption('date');
let activeView = ec.getOption('view');
// Update any option at runtime — the calendar re-renders to reflect the new value
ec.setOption('view', 'dayGridMonth');
ec.setOption('slotDuration', '00:15:00');
ec.setOption('firstDay', 1);
// Add a new event to the calendar and get back the parsed Event object
let added = ec.addEvent({
id: 'task-77',
title: 'Product Demo',
start: '2026-04-15 13:00:00',
end: '2026-04-15 14:00:00',
backgroundColor: '#27ae60'
});
// Retrieve a single Event object by its id — returns null if not found
let ev = ec.getEventById('task-77');
// Retrieve all events currently held in the calendar's internal storage
let allEvents = ec.getEvents();
// Remove a single event by id
ec.removeEventById('task-77');
// Update a single event — the calendar matches by the event's id property
ec.updateEvent({
id: 'standup-1',
title: 'Team Standup (rescheduled)',
start: '2026-04-07 09:30:00',
end: '2026-04-07 10:00:00'
});
// Refetch events from all configured event sources
ec.refetchEvents();
// Refetch resources from a URL or custom function
ec.refetchResources();
// Move the displayed date forward by one period (day, week, month, etc.)
ec.next();
// Move the displayed date backward by one period
ec.prev();
// Return the current View object
// Properties: type, title, currentStart, currentEnd, activeStart, activeEnd
let view = ec.getView();
console.log(view.type, view.currentStart, view.activeEnd);
// Get calendar date/time data for specific screen coordinates
// Useful for finding which date was clicked inside a multi-day event
ec.setOption('eventClick', function(info) {
let point = ec.dateFromPoint(info.jsEvent.clientX, info.jsEvent.clientY);
if (point) {
// point.date is a Date object; point.allDay is a boolean; point.resource is the resource if applicable
console.log('Clicked on date:', point.date, '— allDay:', point.allDay);
}
});
// Programmatically clear the current selection
ec.unselect(); 10. Add the ec-dark class to any parent element of the calendar to activate Dark Mode. For automatic switching based on the operating system’s color scheme preference, use ec-auto-dark instead. The calendar reads the prefers-color-scheme media query and switches themes accordingly.
<body class="ec-dark"> <div id="team-schedule"></div> </body>
12. Override the default CSS variables to customize the themes.
.ec {
color-scheme: light;
/* Main colors */ --ec-color-400: oklch(70.8% 0 0);
--ec-color-300: oklch(87% 0 0);
--ec-color-200: oklch(92.2% 0 0);
--ec-color-100: oklch(97% 0 0);
--ec-color-50: oklch(98.5% 0 0);
/* General props */ --ec-bg-color: #fff;
--ec-text-color: currentcolor;
--ec-border-color: var(--ec-color-300);
/* Buttons */ --ec-button-bg-color: var(--ec-bg-color);
--ec-button-border-color: var(--ec-border-color);
--ec-button-text-color: var(--ec-text-color);
--ec-button-active-bg-color: var(--ec-color-200);
--ec-button-active-border-color: var(--ec-color-400);
--ec-button-active-text-color: var(--ec-button-text-color);
/* Days */ --ec-today-bg-color: oklch(98.7% 0.026 102.212);
--ec-highlight-color: oklch(98.4% 0.019 200.873);
/* Events */ --ec-event-bg-color: oklch(70.7% 0.165 254.624);
--ec-event-text-color: #fff;
--ec-bg-event-color: var(--ec-color-300);
--ec-bg-event-opacity: 0.3;
--ec-event-col-gap: .375rem;
/* Now Indicator */ --ec-now-indicator-color: oklch(63.7% 0.237 25.331);
/* Popup */ --ec-popup-bg-color: var(--ec-bg-color);
.ec-dark & {
color-scheme: dark;
--ec-color-400: oklch(43.9% 0 0);
--ec-color-300: oklch(37.1% 0 0);
--ec-color-200: oklch(26.9% 0 0);
--ec-color-100: oklch(20.5% 0 0);
--ec-color-50: oklch(14.5% 0 0);
--ec-bg-color: var(--ec-color-100);
--ec-today-bg-color: oklch(28.6% 0.066 53.813);
--ec-highlight-color: oklch(30.2% 0.056 229.695);
--ec-bg-event-opacity: 0.5;
}
@media (prefers-color-scheme: dark) {
.ec-auto-dark & {
color-scheme: dark;
--ec-color-400: oklch(43.9% 0 0);
--ec-color-300: oklch(37.1% 0 0);
--ec-color-200: oklch(26.9% 0 0);
--ec-color-100: oklch(20.5% 0 0);
--ec-color-50: oklch(14.5% 0 0);
--ec-bg-color: var(--ec-color-100);
--ec-today-bg-color: oklch(28.6% 0.066 53.813);
--ec-highlight-color: oklch(30.2% 0.056 229.695);
--ec-bg-event-opacity: 0.5;
}
}
} The post Full-Featured Drag-and-Drop JavaScript Event Calendar with Resource & Timeline Views appeared first on CSS Script.
As part of its outstanding 1-day video game sale that includes Nintendo Switch peripherals and…
Panelize is a vanilla JS library that allows you to navigate between large images by…
Panelize is a vanilla JS library that allows you to navigate between large images by…
LANSING, MI (WOWO) Michigan lawmakers say they have reached an agreement to preserve the licenses…
Kali Linux 2026.1 has officially been released, marking the first major update of the year…
A sophisticated supply chain attack targeting Aqua Security’s widely used open-source vulnerability scanner, Trivy. A…
This website uses cookies.