Add-to-Calendar Button

Your next Add-to-Calendar Button

The convenient JavaScript snippet, which lets you reliably create beautiful buttons, where people can add events to their calendars.

#1 Product of the Day on ProductHunt

Code Quality Build Status npm Installations jsDelivr npm Hits

This is for everybody, who wants to include a button at his/her website or app, which enables users to easily add a specific event to their calendars. It’s main goal is to keep this process as easy as possible at maximum compatibility. Simply define your button configuration and everything else is automatically generated by the script. Supporting calendars at Apple, Google, Microsoft (365, Outlook, Teams), Yahoo, and generic iCal.

Supported Calendars

The script integrates easily with any usual HTML webpage (VanillaJS way) as well as popular JavaScript frameworks and libraries like Angular, React, Vue, Svelte, and more.

Supported Technology

In terms of system support, all modern browsers (Chrome, Edge, Firefox, Safari) on Windows, Mac, Android, and iOS as well as rather restricted webview environments like the Instagram in-app browser are supported.

▶️ Demo

See for a live demo.

And remember to ⭐star this repository in order to save it for later! ?

✨ Features

  • Simple and convenient integration of multiple buttons, configurable directly within the HTML code.
  • Optimized and adjustable UX (for desktop and mobile).
  • Beautiful UI (the best combined from experts around the world).
  • Up-to-date integration of all popular calendars:
    • Google Calendar.
    • Yahoo Calender.
    • Microsoft 365, Outlook, and Teams.
    • Automatically generated iCal/ics files (for all other calendars, like Apple).
  • Timed and all-day events.
  • Recurring events (where supported by the calendar)
  • Translatable labels and dynamic dates.
  • Dynamic dates and timezone.
  • Auto-generated rich (structured) data for better SEO.
  • Full support for mouse, touch, or keyboard input.
  • Well documented code, to easily understand the processes and build on top of it.

Demo Screenshot

? Installation / Setup

Option 1: simple (CDN)

You can use the jsDeliver CDN and load the respective ressources into your web project.

Put the css (use atcb-3d for an alternative style) into the <head>:

<link rel="stylesheet" href="">

… and the javascript into the <body> footer:

<script src="" async defer></script>

Mind that this always pulls the latest version! You can pin a specific one by adding “@” + the version number after “button” – see for details. If you want to rather host it yourself, you could also download the source files or clone the repository (mind to take the original one at and maintain/update it manually.

Option 2: npm

Import the package using the following npm command:

npm install add-to-calendar-button

Import the module into your project/component. For example with Angular/React:

import { atcb_action, atcb_init } from 'add-to-calendar-button';

Either use atcb_action with your own buttons/forms/etc, or run atcb_init after the DOM has been loaded. To determine the right moment to execute, … – with Angular, you would use ngAfterViewInit() with atcb_init(); (mind that, depending on your app, other hooks might be better); – with React, you might want to include an event listener like document.addEventListener('DOMContentLoaded', atcb_init, false); or using hooks in a functional component like useEffect(() => atcb_init());

Include the css. For example with Angular or React, add @import 'add-to-calendar-button/assets/css/atcb.min' to some other css file – or include it in other more direct ways (like adding import 'add-to-calendar-button/assets/css/atcb.css'; to the respective component) (use atcb-3d for an alternative style).

?️ Configuration

A button can be easily created by placing a respective placeholder, wherever you want the button to appear. (The style="display:none;" theoretically is not necessary, but should be used for better compatibility.)

<div class="atcb" style="display:none;">(...)</div>

Within this placeholder, you can easily configure the button, by placing a respective JSON structure. Mind that with Angular, you might need to escape the { with {{ '{' }} and } with {{ '}' }}; with React it would be { '{' } and { '}' }.

Minimal structure (required)

If there is no endDate set, the script assumes that it is the same as startDate.

Mind that for auto-generating rich snippets, a location would be mandatory as well.

<div class="atcb" style="display:none;">
    "name":"Add the title of your event",

Full structure

(More hidden options are described further below.)

<div class="atcb" style="display:none;">
    "name":"Add the title of your event",
    "description":"A nice description does not hurt",
    "location":"Somewhere over the rainbow",
    "label":"Add to Calendar",

React examples


If you can’t or don’t want to use atcb_init, you can use the atcb_action import with your own buttons or other elements/components.

This may work better with React and other frontend frameworks, but it misses the and button specific functionalities.

import React from 'react';
import { atcb_action } from 'add-to-calendar-button';

const MyComponent = () => {
  const [name, setName] = React.useState("Some event");
  const changeName = e => {
  return (
    <form onSubmit={e => {
        startDate: "2022-10-14",
        endDate: "2022-10-18",
        options: ['Apple', 'Google', 'iCal', 'Microsoft365', '', 'Yahoo'],
        timeZone: "Europe/Berlin",
        iCalFileName: "Reminder-Event",
      <input value={name} onChange={changeName} />
      <input type="submit" value="save" />


Alternatively, you could use atcb_init with a useEffect hook:

import React from "react";
import { atcb_init } from "add-to-calendar-button";
import 'add-to-calendar-button/assets/css/atcb.css';

const MyComponent = () => {
  React.useEffect(atcb_init, []);
  return (
    <div className="atcb">
      { '{' }
        "name":"Add the title of your event",
        "description":"A nice description does not hurt",
        "location":"Somewhere over the rainbow",
        "label":"Add to Calendar",
      { '}' }

Important information and hidden features

  • The label is optional, but enables you to customize the button text. Default: “Add to Calendar”.
  • Dates need to be formatted as YYYY-MM-DD (ISO-8601).
  • You can also use the word today as date. It will then dynamically use the current day at click.
  • Add +5 at the end of the date to dynamically add 5 days (or any other number). 2022-01-30+12 would generate the 11th of February 2022. This can be interesting, when combined with today.
  • Times are optional. If not set, the button generates all-day events. If set, they need to be formatted as HH:MM
  • You can and should add a timeZone (TZ name). Find a list of them at Wikipedia.
  • Alternatively, you can set the timeZoneOffset, which will always override the timeZone. If neither of them is set, the date refers to UTC time.
  • The timeZone option is recommended since it considers things like summer/winter time, but might not work in very old browsers. timeZoneOffset works with older browsers, but is quite static.
  • Use “currentBrowser” as value for timeZone to dynamically use the time of the user’s browser. Use this with caution, since it would mean that the date and time will differ per user, which should not be the usual case! (Requires all times to be set.)
  • Use the recurrence option to define recurring events. Use can use any valid RRULE to define the respective rule (click here for a generator). But mind that this will deactivate the Yahoo, Microsoft365, Teams, and Outlook options, since they do not support it at the moment (users could still use iCal in this case).
  • If you want to rename a label, use the following schema at the options: optionName + Pipe + yourLabel. “Google|Google Kalender” would generate a Google Calendar option, but label it as “Google Kalender”.
  • There is almost no static text block. For the tiny little rest, you can define a translation by using the language option. Supported options and therefore languages: en (default), de.
  • In case you want to customize them, you can alternatively set the option customLabels and provide a JSON object where the key would be the text block’s identifier (lower case without spaces). See the bottom of the js file for possible text blocks. You can use HTML pseudo tags, which get transformed automatically (but not for styled labels). Suported ones: [url] (see next point), [br], [hr], [p], [strong], [u], [i], [em], [li], [ul], [ol], [h*] (like h1, h2, h3, …).
  • Formatting a URL in the description like [url]https://....[/url] makes it clickable. [url]https://....|URL Text[/url] defines a linked textblock saying “URL Text” (not supported by Apple, iCal, and Yahoo; not supporting special characters).
  • You can set the trigger to click. This makes the button open on click at desktop. Otherwise, the default would be to open on hover. On touch devices, this makes no difference.
  • If you want to define a specific name for any generated ics file (iCal), you can specify it via the iCalFileName option. The default would be “event-to-save-in-my-calendar”.
  • ics files are generated on the fly. However, if you want to go more stable, you can also explicitly define a self-hosted file, setting its absolute path with the icsFile option.
  • You can use the option "inline":true in order to make the button appear with inline-block instead of block style.
  • Use "background":false if you want to be the background overlay to be fully transparent.
  • The default style for the options list, using the regular button, would be a dropdown. You can set the option listStyle to “modal” in order to force the modal version (this would also force the click trigger).
  • If you require line breaks within the description, use \n or <br>.
  • If you set at least a name, startDate, and location, the script automatically generates rich data. Use a URL for the location and it will be labeled as online event.
  • Each generated button and option has a speaking id to be used for any tracking methods. Scheme: “atcb-btn-IDENTIFIER” or “atcb-btn-IDENTIFIER-google” (for the Google option) respectively. The IDENTIFIER will be an automatic number, but can be overridden by providing the option "identifier":"xyz" (no special characters allowed; needs to be unique).
  • Each button comes with a dark and light mode. Set the option lightMode to “dark” or “light” explicitly, or use “system” to automatically adapt to the user’s default setting. You can also use “bodyScheme” to look for the class “atcb-dark” at the body tag and connect the button dynamically to the style of your website.
  • The default layout is more or less basic. Load the atcb-3d.min.css instead to have some more animations and depth.

⚡ Changelog

Find all changes in the dedicated file at

? Contributing

Anyone is welcome to contribute, but mind the guidelines:

IMPORTANT NOTE: Run npm install, npm run format, and npm run build to auto-lint, create the minified js and css files, its sourcemap files as well as the npm_dist/ folder and content!

? Copyright and License

Copyright (c) Jens Kuerschner.

Licensed under MIT license (with “Commons Clause” License Condition v1.0).

? Why this repo exists

While building a personal wedding page, I was confronted with the task to include a button, where invited people could save the event to their calendars. I did not want to build this from scratch (first) and therefore started the usual web research. Unfortunately, all I found where some extremely outdated code snippets, which did not really fit all the modern systems and calendar tools. Beside that, there was only the solution by – all over the place. I was looking at CodePen and all I found where thousands of pens, which basically only included the AddEvent tool, which is hardly connected to their paid services.

The problems with this and other solutions:

  • It holds tons of features, which I did not need. They make sense for companies (also because a server-based solution is more reliable), but not for most small personal dev projects.
  • It limited the free tier to 50 event adds per month (consider the wedding case – this is way too less).
  • It brings some data privacy issues, since you basically send your users to their not fully data secured service. GDPR alert, if you do not inform people about this fact and the underlying Google services!
  • I did not like the UX/UI.
  • Other solutions where no longer up-to-date or have never been fully functional (there are so many edge cases).

Bottom line: Paying for features, which I did not need – at additional privacy concerns – that made me create this solution (for you).

Fun fact: AddEvent changed a lot in the meantime. They slightly increased the free tier and also published some code on GitHub (but still require their service and also not open source!). This might be cool for businesses, but a killer for smaller projects. Anyhow, it is moving into the right direction ?.

? Kudos go to as well as all contributors:


View Github