Symfony UX Calendar Link
EXPERIMENTAL This component is currently experimental and is likely to change, or even change drastically.
Symfony UX CalendarLink is a Symfony bundle that allows generation of "Add to calendar" links for Google
Calendar, Outlook.com, Office 365 and iCalendar (.ics), the format
consumed by Apple Calendar, Outlook desktop, Thunderbird and every native
calendar client.
Installation
Install the bundle using Composer and Symfony Flex:
1
$ composer require symfony/ux-calendar-link
Usage
Start by creating a CalendarEvent object in your controller. Once populated with your event details, pass it to Twig to render the calendar links:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
// src/Controller/EventController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\UX\CalendarLink\CalendarEvent;
final class EventController extends AbstractController
{
#[Route('/event/{id}', name: 'app_event_show')]
public function show(): Response
{
// Build from your database, API, etc.
$event = new CalendarEvent(
title: 'Symfony Live Paris',
start: new \DateTimeImmutable('2026-05-14 09:00'),
end: new \DateTimeImmutable('2026-05-15 18:00'),
location: 'Cité Universitaire Paris',
description: 'Annual Symfony conference in France',
);
return $this->render('event/show.html.twig', ['event' => $event]);
}
}
Then use the two Twig functions to render links:
ux_calendar_link(event, provider): generates a link for one providerux_calendar_links(event): generates links for every registered provider
1 2 3 4 5 6 7 8
{# templates/event/show.html.twig #}
<a href="{{ ux_calendar_link(event, 'google').url }}">Add to Google Calendar</a>
<ul class="add-to-calendar">
{% for link in ux_calendar_links(event) %}
<li><a href="{{ link.url }}">{{ link.label }}</a></li>
{% endfor %}
</ul>
Supported providers
| Field | Outlook | Office 365 | ICS | |
|---|---|---|---|---|
| title | yes | yes | yes | yes |
| start / end | yes | yes | yes | yes |
| description | yes | yes | yes | yes |
| location | yes | yes | yes | yes |
| all-day | yes | yes | yes | yes |
| reminders (VALARM) | - |
- |
- |
yes |
| recurrence (RRULE) | yes | - |
- |
yes |
Fields marked - are silently ignored when generating a link for that
provider, because the provider's URL scheme does not support them. For example,
reminders are honoured only in the .ics output since Google and Outlook
deeplink URLs cannot carry VALARM data.
Reminders and recurrence
Both recurrence rules and reminders can be attached when constructing the event:
1 2 3 4 5 6 7 8 9 10 11
use Symfony\UX\CalendarLink\CalendarEvent;
use Symfony\UX\CalendarLink\CalendarRecurrence;
use Symfony\UX\CalendarLink\CalendarReminder;
$event = new CalendarEvent(
title: 'Weekly sync',
start: new \DateTimeImmutable('2026-05-14 10:00'),
end: new \DateTimeImmutable('2026-05-14 10:30'),
recurrence: CalendarRecurrence::weekly(count: 10),
reminders: [CalendarReminder::before(minutes: 15)],
);
CalendarRecurrence exposes one static factory per RFC 5545 frequency:
minutely(), daily(), weekly(), monthly(), yearly(), each
accepting the usual interval, count and until named arguments. For example:
1 2 3 4 5 6 7 8
// every other day
CalendarRecurrence::daily(interval: 2);
// six monthly occurrences
CalendarRecurrence::monthly(count: 6);
// yearly until 2030
CalendarRecurrence::yearly(until: new \DateTimeImmutable('2030-01-01'));
Note
CalendarRecurrence::minutely() produces a valid RFC 5545 RRULE, but
Google Calendar and Outlook deeplink URLs do not support FREQ=MINUTELY
and will silently ignore it. Use minutely() only when targeting the
ics provider.
CalendarReminder has a single before() factory. Pass any combination
of weeks, days, hours, and minutes as named arguments, with
an optional description. All units are summed:
1 2 3 4 5 6 7 8 9 10 11
// 15 minutes before
CalendarReminder::before(minutes: 15);
// 2 hours before, with a description
CalendarReminder::before(hours: 2, description: 'Leave early');
// one day before
CalendarReminder::before(days: 1);
// 1 hour and 30 minutes before
CalendarReminder::before(hours: 1, minutes: 30);
All-day events
Pass allDay: true to create an event with no specific start or end time:
1 2 3 4 5 6
$holiday = new CalendarEvent(
title: 'Bastille Day',
start: new \DateTimeImmutable('2026-07-14'),
end: new \DateTimeImmutable('2026-07-14'),
allDay: true,
);
Backward compatibility promise
This bundle follows the same backward-compatibility promise as Symfony itself.