# ical.js parses calendars in the browser, with the timezone data left out

> ical.js is a dependency-free JavaScript parser for iCalendar, jCal, vCard and jCard, whose algorithms were taken from libical and whose original goal was to replace libical in the Mozilla Calendar Project. The design decision that shapes every use of it is that no timezones are registered in the stock build, because the IANA data adds weight and changes often, so timezone handling needs a separate step.

**kewisch/ical.js** — Javascript parser for ics (rfc5545) and vcard (rfc6350) data

- Repository: https://github.com/kewisch/ical.js
- Website: https://kewisch.github.io/ical.js/
- Stars: 1,181 · Forks: 161
- Language: JavaScript
- License: MPL-2.0
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/kewisch-ical-js

## A port of libical with the web in mind

ical.js parses four formats, the ones defined in rfc 5545 for iCalendar, rfc 7265 for jCal, rfc 6350 for vCard and rfc 7095 for jCard. Its history explains the shape of the code. The initial goal was to use it as a replacement for libical in the Mozilla Calendar Project, but the library has been written with the web in mind, and the README says it enables you to do all sorts of cool experiments with calendar data and the web. Most of the algorithms were taken from libical, and the project asks something specific of contributors: if you are bugfixing the library, check whether the fix can be upstreamed to libical. That request is the clearest statement of the relationship between the two projects, and it also tells you where the correct place for a fix lives for anyone who has to choose. The audience is a developer with calendar data in the browser or in Node, and the alternative to using this library is a native libical build or a server-side parser, which is exactly what the Mozilla project was trying to avoid.

## No dependencies, and two builds for the browser

The package has no dependencies and is written in modern JavaScript. For Node it installs from npm and is imported as a module:

```bash
npm install ical.js
```

```javascript
import ICAL from "ical.js";
```

For a browser, the library is an ES6 module, and the README is explicit that you should be aware of this, with the example loading it from unpkg:

```html
<script type="module">
  import ICAL from "https://unpkg.com/ical.js/dist/ical.min.js";
  document.querySelector("button").addEventListener("click", () => {
    ICAL.parse(document.getElementById("txt").value);
  });
</script>
```

If you need a plain script tag, there is a transpiled ES5 version, and the README recommends the minified builds for browser use:

```html
<script src="https://unpkg.com/ical.js/dist/ical.es5.min.cjs"></script>
<textarea id="txt"></textarea>
<button onclick="ICAL.parse(document.getElementById('txt').value)"></button>
```

The package metadata marks the module as type module, with the main entry at dist/ical.js and type definitions at dist/types/module.d.ts, and it requires Node 10 or newer. The two builds exist for a practical reason, since a script tag without a type attribute cannot load an ES module, and a library used on a page you do not control may not get a module type.

## The timezones are deliberately not in the box

The one piece of behaviour that surprises people is stated in its own section. The stock ical.js does not register any timezones, because of the additional size it brings. If you want to do timezone conversion and the timezone definitions are not included in the respective ics files, you need to use ical.timezones.js or its minified counterpart, and that file is not included in the distribution since it pulls in IANA timezones that might change regularly. The README points to the GitHub Actions that build your own timezones during CI, or to a recent build from the main branch. The reasoning is sound, because the IANA database is updated many times a year and a calendar library that shipped a frozen copy would silently produce wrong offsets after a daylight saving change. The cost is that timezone support is now a build step rather than an import, and an application that assumes a calendar library knows about Europe/Berlin will be wrong until it loads the timezone file. The samples directory includes a timezone_from_file.ics file, which is the shape of the case this exists for.

## A validator and a recurrence tester you can open in a browser

Two browser tools ship with the project, and they are the fastest way to understand how the library behaves. The ICAL validator demonstrates how to use the library in a webpage and helps verify iCalendar and jCal, and it is hosted online with a jsfiddle set up for trying the library out immediately. The recurrence tester calculates occurrences based on a RRULE, and the README says it can be used to aid in creating test cases for the recurrence iterator, which is the important use, since a recurrence rule is the part of the format with the most room for disagreement between implementations. A parser for a calendar format is only as good as its handling of recurrence exceptions, and a tool that shows the expansion of a rule gives you a way to write a test that asserts on something visible rather than on something you inferred from a specification. The tests in the repository lean on that idea, with sample files that each isolate one rule.

## The test matrix, and how the browser suite runs

The test suites are laid out as separate npm scripts, and the naming tells you how the project thinks about correctness. There is a Node unit suite and a Node acceptance suite, a performance comparison suite, and a browser unit and acceptance suite, plus a combined test script that runs the Node unit and acceptance tests and is described as fast and covering most aspects, and a test-all script for everything:

```bash
npm run test-unit
npm run test-acceptance
npm run test-performance
npm run test-browser
npm run test
npm run test-all
```

The browser tests run the unit and acceptance suites in Chromium, Firefox and WebKit through Playwright, and you run npx playwright install once to download the browsers. To run a single engine you pass its name as a group, for example npm run test-browser -- --group webkit, which is worth knowing because a cross-browser calendar parser is exactly the code where a date parsing difference between engines shows up. Code coverage is generated automatically for the Node unit tests and published to coveralls, and the documentation can be generated locally with npm run jsdoc into the docs/api/ directory, which the README notes is also useful to make sure the jsdoc you wrote is valid.

## The samples directory is a list of the things that break

The repository includes a samples/ directory with a set of .ics files that read like a list of the ways calendar data goes wrong in the wild. There is a blank_line_end.ics and a blank_line_mid.ics, a duration_instead_of_dtend.ics, a forced_types.ics, a google_birthday.ics, a minimal.ics, a multiple_rrules.ics, only_dtstart_date.ics and only_dtstart_time.ics, a parserv2.ics, rdate_exdate.ics, recur_instances.ics, recur_instances_finite.ics, a timezone_from_file.ics and utc_negative_zero.ics, along with a timezones/ subdirectory. Every one of those names is a specification edge case that a parser has to decide about, and having them as files rather than as prose in an issue means the behaviour is pinned by a test. That is the pattern that makes this library dependable for its stated purpose, and it is the reason the acceptance suite exists as a separate thing from the unit suite, because these are not isolated functions but whole documents that have to parse end to end. The build story is the last piece, with npm run build producing the single-file browser build, its minified counterpart and the source map.

## Where ical.js is the wrong tool

Two situations rule it out. The first is a fixed environment that has to agree with other calendar software, and here the maintainers' own instruction applies, that a fix you find should be checked for upstreaming to libical, since the algorithms were taken from libical, and patching the JavaScript port alone leaves the two diverging. If you are building something that has to interoperate with a wide range of clients, the safe move is to use libical rather than a port of it, and to use ical.js only where you need it in a browser and cannot run native code. The second is a server-side batch job that processes millions of calendars, where the overhead of shipping a JavaScript runtime and its IANA timezone data becomes the dominant cost. The library itself is not the problem in either case, the mismatch between the port and the reference implementation is. The licence is the Mozilla Public License version 2.0, which is a file-level copyleft that suits a library you vendor into an application, and the last release listed is v2.2.1 from 2025-08-08 with the last push on 2026-10-13.

## Conclusion

ical.js is the right library when you need to read or write calendar and contact data in a browser or in Node without a native dependency, because the recurrence handling is a port of libical rather than a from-scratch implementation, and the project keeps a validator and a recurrence tester online so you can check behaviour against the format rather than against a blog post. It is the wrong choice when you need the timezones included, since the stock build registers none and you have to supply them yourself, and the wrong choice when you are fixing a parsing bug and can patch libical itself, because the maintainers ask that fixes be upstreamed to libical first. Before adopting it, decide where the timezone data comes from, since the file is not shipped in the distribution and has to be built during CI or taken from a recent main branch build, and pin a version, because the last release was v2.2.1 on 2025-08-08.

## FAQ

### Which formats does ical.js parse?

It parses the formats defined in rfc 5545 for iCalendar, rfc 7265 for jCal, rfc 6350 for vCard and rfc 7095 for jCard, along with their extensions.

### How do I install and import ical.js?

Run npm install ical.js and import it with import ICAL from "ical.js". In a browser it is an ES6 module, so you import it from a URL such as https://unpkg.com/ical.js/dist/ical.min.js, or you use the transpiled ES5 build at https://unpkg.com/ical.js/dist/ical.es5.min.cjs for a plain script tag.

### Does ical.js include timezone definitions?

No. The stock ical.js does not register any timezones because of the additional size, and the file with the IANA definitions is not included in the distribution since it pulls in timezones that might change regularly. You need ical.timezones.js or its minified counterpart, built during CI or taken from a recent main branch build.

### How do I run the browser tests for ical.js?

With npm run test-browser, which runs the unit and acceptance suites in Chromium, Firefox and WebKit via Playwright. Run npx playwright install once to download the browsers, and pass an engine name as a group to run one, for example npm run test-browser -- --group webkit.

## Sources

- [kewisch/ical.js on GitHub](https://github.com/kewisch/ical.js)
- [License: MPL-2.0](https://github.com/kewisch/ical.js/blob/main/LICENSE)
- [Project website](https://kewisch.github.io/ical.js/)
- [README](https://github.com/kewisch/ical.js/blob/main/README.md)
- [Releases](https://github.com/kewisch/ical.js/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/kewisch-ical-js
