iCal.js - iCalendar and vCard Parser
ical.js is a JavaScript library designed for parsing and manipulating iCalendar (RFC 5545) and vCard (RFC 6350) data, along with their JSON counterparts jCal (RFC 7265) and jCard (RFC 7095). The library is currently at version 2.2.1 and maintains an active development cycle, regularly releasing updates that include bug fixes, performance improvements, and TypeScript type enhancements. Originally ported from libical with a focus on web compatibility, it provides a robust, dependency-free solution for handling calendar and contact data. A key differentiator is its dual distribution, offering both ES6 modules for modern environments and a transpiled ES5 CommonJS build for broader compatibility, particularly in older browser script tags. It also offers an optional, separate bundle for comprehensive IANA timezone definitions, acknowledging their size and update frequency.
Common errors
-
ReferenceError: ICAL is not defined
cause Attempting to use `ical.js` (v2.0.0+) in a CommonJS environment with `require()` or directly in a browser without `type="module"` or the ES5 build.fixEnsure you are using `import ICAL from 'ical.js';` in module-aware environments or include the `ical.es5.cjs` bundle for legacy browser script tags. -
TypeError: Cannot read properties of undefined (reading 'start') or incorrect recurrence calculations
cause This typically occurs when dealing with recurring events that rely on timezone data, but the `ical.timezones.js` bundle has not been included, or timezone definitions are missing from the ICS file itself.fixInclude `ical.timezones.js` in your project for comprehensive IANA timezone support. If issues persist, verify that the iCalendar data itself contains VTIMEZONE components if specific timezones are expected. -
ICAL.parse: expected ':' but found '...' at line X
cause The input iCalendar or vCard string is malformed or contains syntax errors according to RFC specifications (e.g., missing colons, invalid property names, incorrect folding).fixReview the iCalendar/vCard string for syntax compliance. Tools like the `ical.js` online validator (`kewisch.github.io/ical.js/validator.html`) can help identify specific parsing issues and correct formatting errors.
Warnings
- breaking `ical.js` transitioned to ES6 modules in v2.0.0. This change breaks direct CommonJS `require()` usage with the main bundle, requiring module-aware environments or specific ES5 builds.
- gotcha The main `ical.js` package does not include IANA timezone definitions by default to reduce bundle size. Date calculations and recurrence expansions involving timezones will be inaccurate or incorrect without them.
- gotcha Direct usage of the `ical.js` ES6 module via a browser `<script>` tag requires the `type="module"` attribute. Failing to include this or attempting to use it in older browser environments will lead to module loading errors.
Install
-
npm install ical.js -
yarn add ical.js -
pnpm add ical.js
Imports
- ICAL
const ICAL = require('ical.js');import ICAL from 'ical.js';
- Component
import Component from 'ical.js/Component';
import { Component } from 'ical.js'; - Time
import Time from 'ical.js/Time';
import { Time } from 'ical.js'; - Types
import type { Component, Event, Time } from 'ical.js';
Quickstart
import ICAL from "ical.js";
async function parseAndDisplayCalendar() {
const icsData = `BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:-//Example Corp//NONSGML My Calendar//EN\nCALSCALE:GREGORIAN\nBEGIN:VEVENT\nUID:event1@example.com\nDTSTAMP:20230101T120000Z\nDTSTART:20230115T100000Z\nDTEND:20230115T110000Z\nSUMMARY:Team Meeting\nDESCRIPTION:Discuss Q1 planning and objectives.\nLOCATION:Conference Room A\nEND:VEVENT\nBEGIN:VEVENT\nUID:event2@example.com\nDTSTAMP:20230101T130000Z\nDTSTART:20230220T140000Z\nDURATION:PT1H30M\nSUMMARY:Project Deadline Review\nRRULE:FREQ=MONTHLY;COUNT=3\nEND:VEVENT\nEND:VCALENDAR`;
try {
const jcalData = ICAL.parse(icsData);
const comp = new ICAL.Component(jcalData);
console.log("Calendar events:");
const events = comp.getAllSubcomponents('vevent');
if (events.length === 0) {
console.log("No events found.");
return;
}
events.forEach((eventComp, index) => {
const event = new ICAL.Event(eventComp);
console.log(`\nEvent ${index + 1}:`);
console.log(` Summary: ${event.summary}`);
console.log(` Location: ${event.location || 'N/A'}`);
console.log(` Start: ${event.startDate.toString()}`);
if (event.isRecurring()) {
console.log(` (Recurring Event - Rule: ${event.getFirstPropertyValue('rrule') ? event.getFirstPropertyValue('rrule').toString() : 'N/A'})`);
const iterator = event.iterator();
let occurrences = [];
let next;
// Get up to 5 occurrences
for (let i = 0; i < 5 && (next = iterator.next()); i++) {
occurrences.push(new ICAL.Time(next).toString());
}
if (occurrences.length > 0) {
console.log(` First 5 occurrences: ${occurrences.join(', ')}`);
}
} else {
console.log(` End: ${event.endDate.toString()}`);
}
});
} catch (error) {
console.error("Error parsing iCalendar data:", error);
}
}
parseAndDisplayCalendar();