
CalendarJS is a feature-rich JavaScript calendar library that allows you to create calendars, date pickers, event schedules, and timelines in modern web apps.
Features:
- Generate modal, inline, auto, and picker style calendars.
- Pick single dates, date ranges, and date & time values.
- Restrict selectable dates with valid date windows.
- Change weekday order for local business calendars.
- Show event markers inside the calendar grid.
- Build day, week, and weekday schedule views.
- Drag and resize schedule items in the grid.
- Track schedule history with undo and redo actions.
- Block editing in protected time ranges.
- Switch timeline items across left, right, top, and bottom layouts.
- Filter timeline output by month in monthly mode.
- Convert ISO dates to Excel-style serial numbers.
- Format dates into relative time labels.
Use cases:
- Build a hotel booking form with date range and time selection.
- Add a weekly staff planner to an internal operations dashboard.
- Show a release history view inside a product changelog page.
- Convert spreadsheet date values in import and export flows.
How to use it:
1. Install the package with NPM and import modules into your project:
npm install @calendarjs/ce
import { Calendar } from '@calendarjs/ce';
import { Schedule } from '@calendarjs/ce';
import { Timeline } from '@calendarjs/ce';2. Or load it from a CDN:
<!-- Load the stylesheet first --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@calendarjs/ce/dist/style.min.css" /> <!-- Load LemonadeJS before CalendarJS in browser builds --> <script src="https://cdn.jsdelivr.net/npm/lemonadejs/dist/lemonade.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/@calendarjs/ce/dist/index.min.js"></script>
3. Create an inline range calendar and a time picker
<div id="report-range"></div>
<input id="publish-at" />
<script>
// Create an inline range calendar for a reporting window
const reportRange = calendarjs.Calendar(document.getElementById('report-range'), {
type: 'inline',
range: true,
grid: true,
startingDay: 1,
validRange: ['2026-04-01', '2026-05-31'],
value: ['2026-04-12', '2026-04-19'],
onchange: function(self, value) {
// Read the selected range after every change
console.log('Range value:', value);
}
});
// Bind a popup calendar to an input field
const publishAt = calendarjs.Calendar(document.getElementById('publish-at'), {
type: 'default',
input: document.getElementById('publish-at'),
time: true,
format: 'MMMM DD, YYYY HH:MI',
value: '2026-04-20 15:45:00',
onchange: function(self, value) {
// Store the picked date and time in your form state
console.log('Publish at:', value);
}
});
// Open the popup calendar from code if you need a custom trigger
publishAt.open();
</script>
4. Build a weekly schedule:
<div id="team-board"></div>
<script>
// Seed the scheduler with a few meetings
const teamBoard = calendarjs.Schedule(document.getElementById('team-board'), {
type: 'week',
value: '2026-04-20',
grid: 30,
overlap: false,
validRange: ['08:00', '19:00'],
data: [
{
guid: 'evt-101',
title: 'Design review',
date: '2026-04-21',
start: '09:00',
end: '10:30',
color: '#b86e2f'
},
{
guid: 'evt-102',
title: 'Ops sync',
date: '2026-04-22',
start: '13:00',
end: '14:00',
color: '#5e7f4a'
}
],
oncreate: function(self, events) {
// Persist new events after user edits
console.log('Created:', events);
},
onchangeevent: function(self, newValue, oldValue) {
// Track updates for autosave or audit logs
console.log('Changed:', newValue, oldValue);
},
onerror: function(self, message) {
// Show validation feedback in your UI
console.log('Schedule error:', message);
}
});
// Lock early morning and late evening blocks
teamBoard.setReadOnly([['00:00', '08:00'], ['19:00', '23:59']]);
// Add an event from code
teamBoard.addEvents({
guid: 'evt-103',
title: 'Client handoff',
date: '2026-04-23',
start: '16:00',
end: '17:00',
color: '#9f5140'
});
</script>
5. Generate a timeline:
<div id="release-timeline"></div>
<script>
// Create a project timeline with dated milestones
const releaseTimeline = calendarjs.Timeline(document.getElementById('release-timeline'), {
type: 'monthly',
align: 'left',
order: 'asc',
controls: true,
data: [
{
title: 'Spec freeze',
subtitle: 'Planning',
description: 'The team locked scope for the next release cycle.',
date: new Date('2026-04-02'),
borderColor: '#b86e2f',
borderStyle: 'solid'
},
{
title: 'QA pass',
subtitle: 'Validation',
description: 'QA finished the final regression pass.',
date: new Date('2026-04-11'),
borderColor: '#5e7f4a',
borderStyle: 'solid'
},
{
title: 'Public launch',
subtitle: 'Release',
description: 'The new build went live to all users.',
date: new Date('2026-04-18'),
borderColor: '#9f5140',
borderStyle: 'dashed'
}
],
onupdate: function(self) {
// React to monthly filtering or data changes
console.log('Timeline updated');
}
});
// Move to the next month in monthly mode
releaseTimeline.next();
releaseTimeline.updateResult();
</script>
6. Use the helper utilities:
// Read the current UTC date string
const nowValue = calendarjs.Helpers.now();
// Convert an ISO date to an Excel style serial number
const serialValue = calendarjs.Helpers.dateToNum('2026-04-20 10:00:00');
// Convert the serial number back to a date string
const restoredDate = calendarjs.Helpers.numToDate(serialValue);
// Turn a timestamp into a relative label
const relativeLabel = calendarjs.Helpers.prettify('2026-04-19 10:00:00');
console.log(nowValue, serialValue, restoredDate, relativeLabel);
Configuration options:
Calendar options
type('default' | 'auto' | 'picker' | 'inline'): Sets the calendar display mode.format(string): Sets the output format for rendered values.range(boolean): Turns range selection on or off.value(number | string): Sets the initial calendar value.numeric(boolean): Returns Excel style serial numbers in place of ISO strings.footer(boolean): Shows or hides the footer controls.time(boolean): Shows the hour and minute picker.grid(boolean): Turns grid mode on or off.placeholder(string): Sets placeholder text for bound inputs.disabled(boolean): Locks the calendar UI.startingDay(number): Sets the first weekday from0to6.validRange(number[] | string[]): Restricts selectable dates to a range.data(Array<{date: string, [key: string]: any}>): Adds event data for calendar markers.wheel(boolean): Turns mouse wheel view changes on or off.input(HTMLInputElement | 'auto'): Binds the calendar to an input element.initInput(boolean): Applies input setup and calendar classes.onchange((self: object, value: string) => void): Runs after the value changes.onupdate((self: object, value: string) => void): Runs after the view updates.onclose((self: object, origin: string) => void): Runs after the modal closes.onopen((self: object) => void): Runs after the modal opens.onChange((e: Event) => void): Exposes a React focused change hook.
Schedule options
type('week' | 'day' | 'weekdays'): Sets the schedule view type.weekly(boolean): Uses weekday columns in place of dated columns.value(string): Sets the initial ISO date inYYYY-MM-DDformat.data(Schedule.Event[]): Loads the schedule with event records.grid(number): Sets the grid step in minutes.overlap(boolean): Permits overlapping events.validRange(string[]): Restricts visible editing hours.readOnlyRange(string[] | string[][]): Marks one or more time ranges as read only.onbeforechange((self: Instance, state: object) => boolean | void): Runs before a grid drag or resize change.onchange((self: Instance, state: object) => void): Runs after a grid state change.onbeforecreate((self: Instance, events: Event | Event[], e?: MouseEvent) => boolean | void): Runs before event creation.oncreate((self: Instance, events: Event | Event[], e?: MouseEvent) => void): Runs after event creation.ondblclick((self: Instance, event: Event) => void): Runs on event double click.onedition((self: Instance, event: Event) => void): Runs when editing starts.ondelete((self: Instance, event: Event) => void): Runs after event deletion.onchangeevent((self: Instance, newValue: Partial<Event>, oldValue: Partial<Event>) => void): Runs after event data changes.onerror((self: Instance, message: string) => void): Runs after validation errors.
Schedule event object
type(string | null): Sets the event type label.title(string): Sets the event title.start(string): Sets the start time inHH:MM.end(string): Sets the end time inHH:MM.guests(string): Stores guest data.location(string): Stores a location label.description(string): Stores descriptive text.color(string): Sets the event color.readonly(boolean): Locks the event from editing.guid(string): Stores the unique event id.date(string): Stores the ISO date whenweeklyisfalse.weekday(number): Stores the weekday from0to6whenweeklyistrue.
Timeline options
data(Timeline.Item[]): Loads the timeline with item records.type("monthly" | string): Sets the timeline mode.align("left" | "right" | "top" | "bottom" | string): Sets item alignment.message(string): Sets the empty state message.order('asc' | 'desc' | undefined): Sets chronological order.width(number): Sets container width.height(number): Sets container height.url(string): Sets a remote data URL.remote(boolean): Turns remote loading on.onupdate((self: Object) => void): Runs after the visible timeline data updates.
Timeline item object
title(string): Sets the main item label.subtitle(string): Sets the secondary label.description(string): Sets the item body text.date(string | Date): Sets the item date.borderColor(string): Sets the item border color.borderStyle(string): Sets the item border style.
API methods:
// Create a calendar instance
const calendar = calendarjs.Calendar(document.getElementById('calendar-root'), {
type: 'inline'
});
// Open the calendar modal
calendar.open();
// Close the calendar modal
calendar.close({ origin: 'button' });
// Check modal state
calendar.isClosed();
// Switch the calendar view
calendar.setView('months');
// Move forward in the current view
calendar.next();
// Move backward in the current view
calendar.prev();
// Reset the selected value
calendar.reset();
// Read the current value
calendar.getValue();
// Write a new value
calendar.setValue('2026-04-24');
// Accept the current selection
calendar.update();
// Create a schedule instance
const schedule = calendarjs.Schedule(document.getElementById('schedule-root'), {
type: 'week'
});
// Add one event
schedule.addEvents({
guid: 'evt-201',
title: 'Roadmap sync',
date: '2026-04-24',
start: '10:00',
end: '11:00',
color: '#b86e2f'
});
// Add multiple events
schedule.addEvents([
{
guid: 'evt-202',
title: 'Editorial review',
date: '2026-04-24',
start: '12:00',
end: '13:00',
color: '#5e7f4a'
},
{
guid: 'evt-203',
title: 'Launch prep',
date: '2026-04-25',
start: '15:00',
end: '16:00',
color: '#9f5140'
}
]);
// Update an event by guid
schedule.updateEvent('evt-201', {
title: 'Roadmap sync v2',
start: '10:30'
});
// Update an event by object reference
const firstRecord = schedule.getData()[0];
schedule.updateEvent(firstRecord, {
end: '11:30'
});
// Delete by guid
schedule.deleteEvents('evt-202');
// Delete by object
schedule.deleteEvents(firstRecord);
// Delete multiple records
schedule.deleteEvents(['evt-201', 'evt-203']);
// Clear the current visual selection
schedule.resetSelection();
// Restrict editable hours
schedule.setRange(['09:00', '18:00']);
// Mark time ranges as read only
schedule.setReadOnly([['00:00', '09:00'], ['18:00', '23:59']]);
// Read all schedule data
schedule.getData();
// Replace all schedule data
schedule.setData([
{
guid: 'evt-301',
title: 'Support block',
date: '2026-04-26',
start: '11:00',
end: '12:00',
color: '#b86e2f'
}
]);
// Read the DOM node for an event
schedule.getEvent('evt-301');
// Render the schedule again
schedule.render();
// Undo the last change
schedule.undo();
// Redo the last undone change
schedule.redo();
// Move to the next period
schedule.next();
// Move to the previous period
schedule.prev();
// Create a timeline instance
const timeline = calendarjs.Timeline(document.getElementById('timeline-root'), {
type: 'monthly',
data: []
});
// Read the bound timeline data
timeline.data;
// Move to the next month in monthly mode
timeline.next();
// Move to the previous month in monthly mode
timeline.prev();
// Rebuild the visible item list
timeline.updateResult();
// Fetch remote data when remote mode is active
timeline.fetchRemote();
// Convert a value to two digits
calendarjs.Helpers.two(7);
// Validate a Date object
calendarjs.Helpers.isValidDate(new Date());
// Validate an ISO date string
calendarjs.Helpers.isValidDateFormat('2026-04-24');
// Convert a Date to a string
calendarjs.Helpers.toString(new Date(), false);
// Convert a string into an array
calendarjs.Helpers.toArray('2026-04-24 12:30:00');
// Convert an array into a string date
calendarjs.Helpers.arrayToStringDate([2026, 4, 24, 12, 30, 0]);
// Convert a Date or string to an Excel style serial
calendarjs.Helpers.dateToNum('2026-04-24 12:30:00');
// Convert an Excel style serial back to a string
calendarjs.Helpers.numToDate(46136.520833333336);
// Convert an Excel style serial to an array
calendarjs.Helpers.numToDate(46136.520833333336, true);
// Convert a timestamp into a relative label
calendarjs.Helpers.prettify('2026-04-23 12:30:00');
// Update every .prettydate node on the page
calendarjs.Helpers.prettifyAll();
// Read the current date string
calendarjs.Helpers.now();
// Read localized weekday names
calendarjs.Helpers.weekdays;
// Read localized short weekday names
calendarjs.Helpers.weekdaysShort;
// Read localized month names
calendarjs.Helpers.months;
// Read localized short month names
calendarjs.Helpers.monthsShort;
// Set custom labels
calendarjs.setDictionary({
Reset: 'Clear',
Done: 'Apply'
});
// Read package metadata
calendarjs.about();
Events:
// Run after a calendar value changes
calendarjs.Calendar(document.getElementById('calendar-a'), {
onchange: function(self, value) {
console.log('Calendar change:', value);
}
});
// Run after the calendar view updates
calendarjs.Calendar(document.getElementById('calendar-b'), {
onupdate: function(self, value) {
console.log('Calendar update:', value);
}
});
// Run after the calendar modal closes
calendarjs.Calendar(document.getElementById('calendar-c'), {
onclose: function(self, origin) {
console.log('Calendar closed from:', origin);
}
});
// Run after the calendar modal opens
calendarjs.Calendar(document.getElementById('calendar-d'), {
onopen: function(self) {
console.log('Calendar opened');
}
});
// React focused change callback
calendarjs.Calendar(document.getElementById('calendar-e'), {
onChange: function(e) {
console.log('React style change event:', e);
}
});
// Run before a schedule drag or resize change
calendarjs.Schedule(document.getElementById('schedule-a'), {
onbeforechange: function(self, state) {
console.log('Before grid change:', state);
return true;
}
});
// Run after a schedule grid change
calendarjs.Schedule(document.getElementById('schedule-b'), {
onchange: function(self, state) {
console.log('Grid changed:', state);
}
});
// Run before creating events
calendarjs.Schedule(document.getElementById('schedule-c'), {
onbeforecreate: function(self, events, e) {
console.log('Before create:', events, e);
return true;
}
});
// Run after creating events
calendarjs.Schedule(document.getElementById('schedule-d'), {
oncreate: function(self, events, e) {
console.log('Created:', events, e);
}
});
// Run on event double click
calendarjs.Schedule(document.getElementById('schedule-e'), {
ondblclick: function(self, event) {
console.log('Double click:', event);
}
});
// Run when event editing starts
calendarjs.Schedule(document.getElementById('schedule-f'), {
onedition: function(self, event) {
console.log('Editing:', event);
}
});
// Run after deleting an event
calendarjs.Schedule(document.getElementById('schedule-g'), {
ondelete: function(self, event) {
console.log('Deleted:', event);
}
});
// Run after event data changes
calendarjs.Schedule(document.getElementById('schedule-h'), {
onchangeevent: function(self, newValue, oldValue) {
console.log('Event changed:', newValue, oldValue);
}
});
// Run after schedule validation errors
calendarjs.Schedule(document.getElementById('schedule-i'), {
onerror: function(self, message) {
console.log('Schedule error:', message);
}
});
// Run after timeline data updates
calendarjs.Timeline(document.getElementById('timeline-a'), {
onupdate: function(self) {
console.log('Timeline updated:', self);
}
});
Alternatives:
- FullCalendar: Handles large calendar apps with month, week, resource, and drag editing views.
- TOAST UI Calendar: Ships a full calendar UI with multiple calendar layers and template hooks.







