How to: Create an iCalender (.ics) File in WordPress

In the past, we’ve written about creating a custom calendar, implementing structured data and adding limits to date picker fields. We’ve also written extensively about the Events Calendar plugin and extending its functionality like checking for venue conflicts. All of these can be used to create a beautiful full-functioning calendar on your website.

But while displaying events on your website is a great way to increase user engagement and promote your mission, wouldn’t it be better if your users could add your events to their own calendars?

This can be achieved using an iCalendar (.ics) file, which is a plain-text, UTF-8 encoded file used to store event data and that can be added to any type of calendar software, be it Apple or Google or Outlook. The iCalendar file uses a standardized format defined in RFC 5545, which is what enables it to be shared across different platforms.

Here’s a basic example of an iCalendar file.

BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Digital Ink//Event Title//EN
BEGIN:VEVENT
UID:1234567890@digital.ink
DTSTAMP:20261027T120000Z
DTSTART:20261028T180000Z
DTEND:20261028T200000Z
SUMMARY:Event Summary
END:VEVENT
END:VCALENDAR

The iCalendar file always starts with BEGIN:VCALENDAR and ends with END:VCALENDAR. The VERSION and PRODID are header properties that define the calendar version and scale. The VEVENT component defines a calendar event, and the properties like DTSTAMP, DTSTART, and DTEND define the event’s start date and time and end date and time. Other relevant properties can be added in the VEVENT if needed.

Let’s walk through the steps of creating an iCalendar file, using the events in your WordPress-powered site.

Creating an HTML Button with the WordPress Event ID

To start, we’ll create a button that, when clicked, creates the iCalendar file and downloads it to our user’s calendar.

The button will use jQuery to make an AJAX call to a PHP function that creates the .ics file, with jQuery downloading the file to our user’s device.

First up, our HTML.

In our single events template, we’ll use the following HTML to create the button which, when clicked, will download the iCalendar file.

<button type="button" class="add-to-calendar" data-id="<?=get_the_ID();?>" data-name="<?=get_post_field('post_name', get_the_ID());?>">Add To Calendar</button>

This HTML creates a button with the add-to-calendar class, a data-id attribute with the value of the WordPress event’s ID (using WordPress’s built-in get_the_ID() function), and a data-name attribute with the value of the slug of the post (using the get_post_field() function).

The jQuery Call to Run a Custom Function

Now, we’ll add this jQuery code to our website’s Javascript file.

$('.add-to-calendar').on('click', function(e){
     e.preventDefault();
     let curr = $(this),
     curr_event_id = curr.attr('data-id');
     if ( curr_event_id ) {
          $.ajax({
               type: 'POST',
               dataType: 'text',
               url: '/wp-admin/admin-ajax.php',
               data: { 
                    action: 'dgtlnk_add_to_calendar_ajax',
                    id: curr_event_id,
               },
               success: function(data){
                    const blob = new Blob([data], { type: 'text/calendar' }),
                    url = window.URL.createObjectURL(blob),
                    a = document.createElement('a');
                    a.href = url;
                    a.download = curr.attr('data-name') + '.ics';
                    document.body.appendChild(a);
                    a.click();
                    window.URL.revokeObjectURL(url);
                    document.body.removeChild(a);
               },
               error : function(jqXHR, textStatus, errorThrown) {
                    console.error(jqXHR + " :: " + textStatus + " :: " + errorThrown);
               }
          });
     }
     return false;
});

In this jQuery code, we attach a click event to our button. When the button is clicked, we make an AJAX call using admin-ajax.php to a custom function called dgtlnk_add_to_calendar_ajax (where we pass the event’s ID). If the AJAX call is successful, using the Blob interface, we create a new Blob object using the data that is returned and assign it to the variable blob.

Using the window.URL.createObjectURL method, we’ll create an object URL pointing to the blob object. Then, we temporarily create an anchor element to download the file to the user’s device and remove it afterwards.

The PHP Function Creating the .ics File

In our site’s functions.php file, we’ll create the dgtlnk_add_to_calendar_ajax function which gets called when the button is clicked.

add_action('wp_ajax_dgtlnk_add_to_calendar_ajax', 'dgtlnk_add_to_calendar_ajax'); 

add_action('wp_ajax_nopriv_dgtlnk_add_to_calendar_ajax', 'dgtlnk_add_to_calendar_ajax');

function dgtlnk_add_to_calendar_ajax() {
    if ( $_POST['id'] ) {
        $curr_id = (int)$_POST['id'];
        $ics_content = "BEGIN:VCALENDAR\r\n";
        $ics_content .= "VERSION:2.0\r\n";
        $ics_content .= "PRODID:-//" . get_bloginfo('name') . "//NONSGML " . get_the_title($curr_id) . "//EN\r\n";
        $ics_content .= "BEGIN:VEVENT\r\n";
        $ics_content .= "UID:" . uniqid() . "@" . str_replace(array('http://', 'https://'), '', home_url()) . "\r\n";
        $ics_content .= "DTSTAMP:" . gmdate('Ymd\THis\Z') . "\r\n";
        $ics_content .= "DTSTART:" . date('Ymd', strtotime(get_field('start_date', $curr_id))) . 'T' . date('H:i:s', strtotime(get_field('start_time', $curr_id))) . "\r\n";
        $ics_content .= "DTEND:" . date('Ymd', strtotime(get_field('end_date', $curr_id))) . 'T' . date('H:i:s', strtotime(get_field('end_time', $curr_id))) . "\r\n";
        $ics_content .= "SUMMARY:" . get_the_excerpt($curr_id) . "\r\n";
        $ics_content .= "END:VEVENT\r\n";
        $ics_content .= "END:VCALENDAR\r\n";
        // Set HTTP headers to force a download
        header('Content-Type: text/calendar; charset=utf-8');
        header('Content-Disposition: attachment; filename="event.ics"');
        echo $ics_content;
    }
    exit;
}

In this PHP code, we add action hooks to fire the authenticated AJAX actions. Then we use the event’s ID for to create the contents of the iCalendar file and return it to the jQuery function making that AJAX call. You’ll notice a handful of custom fields – start_date, start_time, end_date, end_time – adjust these as appropriate for your event’s data.

That’s it! With this code, you can add downloadable iCalendar files so that your users can easily add your events to their personal calendars – no matter what software they use.

If you need any help implementing this, or anything similar, reach out to us.