Vanilla Calendar JS Library for Date Picking, Scheduling, and Timelines

Category: Date & Time , Javascript | April 19, 2026
Authorcomponade
Last UpdateApril 19, 2026
LicenseMIT
Views0 views
Vanilla Calendar JS Library for Date Picking, Scheduling, and Timelines

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 from 0 to 6.
  • 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 in YYYY-MM-DD format.
  • 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 in HH:MM.
  • end (string): Sets the end time in HH: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 when weekly is false.
  • weekday (number): Stores the weekday from 0 to 6 when weekly is true.

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.

You Might Be Interested In:


Leave a Reply