spatie/calendar-links
Generate “Add to calendar” links and ICS files for events. Supports Google Calendar, iCal/Apple Calendar, Outlook and more. Define title, start/end, description and location, then get shareable URLs or downloadable .ics content for your app.
Installation:
composer require spatie/calendar-links
Add to composer.json if using Laravel's autoloader:
"autoload": {
"psr-4": {
"App\\": "app/",
"Spatie\\": "vendor/spatie/"
}
}
Run composer dump-autoload.
First Use Case: Generate a Google Calendar link for an event in a Laravel controller or Blade template:
use Spatie\CalendarLinks\Link;
$eventLink = Link::create(
'Team Meeting',
new \DateTimeImmutable('2023-12-15 10:00:00'),
new \DateTimeImmutable('2023-12-15 11:00:00')
)->google();
return response()->json(['link' => $eventLink]);
Or in Blade:
<a href="{{ $eventLink->google() }}">Add to Google Calendar</a>
Key Files to Reference:
vendor/spatie/calendar-links/src/Link.php (Core class)vendor/spatie/calendar-links/src/Generators/ (Service-specific generators)vendor/spatie/calendar-links/README.md (Examples and API docs)$link = Link::create(
'Product Launch',
new \DateTimeImmutable('2023-11-30 09:00:00'),
new \DateTimeImmutable('2023-11-30 17:00:00')
)->timezone('America/New_York');
createAllDay):
$link = Link::createAllDay(
'Conference Day',
new \DateTimeImmutable('2023-12-01'),
1 // Inclusive days (1 = single day)
);
$link = Link::createAllDay(
'Workshop',
new \DateTimeImmutable('2023-12-05'),
3 // 3-day event (Dec 5-7)
);
Add metadata before generating links:
$link = Link::create('Webinar', $start, $end)
->description('Laravel Performance Tuning')
->address('Online: Zoom Link')
->customProperty('X-CUSTOM', 'value');
Generate links for different calendar providers:
$googleLink = $link->google();
$outlookLink = $link->webOutlook();
$officeLink = $link->webOffice();
$yahooLink = $link->yahoo();
$icsDataUri = $link->ics(); // Data URI for download
$icsUri = $link->ics(['UID' => 'unique-id-123']);
$icsContent = $link->ics([], ['format' => 'file']);
Storage::put('events/event.ics', $icsContent);
$link->ics([
'PRODID' => '-//MyApp//Calendar//EN',
'URL' => 'https://example.com/event',
'REMINDER' => [
'DESCRIPTION' => 'Reminder',
'TIME' => new \DateTimeImmutable('2023-11-29 16:00:00')
]
]);
Extend the package for unsupported services:
use Spatie\CalendarLinks\Generators\Generator;
class CustomGenerator implements Generator
{
public function __invoke(Link $link): string
{
return "https://custom-calendar.com/add?title={$link->title}&start={$link->from->format('Ymd\THis')}";
}
}
$customLink = $link->formatWith(new CustomGenerator());
Blade Directives: Create a Blade directive for reusable calendar links:
// app/Providers/BladeServiceProvider.php
Blade::directive('calendar', function ($expression) {
$link = eval("return {$expression};");
return "href=\"{$link->google()}\"";
});
Usage:
<a @calendar('$eventLink') class="btn">Add to Calendar</a>
Event Model Casting:
Cast event attributes to Link objects:
// app/Models/Event.php
protected $casts = [
'calendar_link' => Link::class,
];
Usage:
$event = Event::find(1);
echo $event->calendar_link->google();
API Responses: Return calendar links in API responses:
return response()->json([
'event' => $event,
'calendar_links' => [
'google' => $link->google(),
'outlook' => $link->webOutlook(),
'ics' => $link->ics(),
],
]);
Mailable Attachments: Attach ICS files to emails:
// app/Mail/EventReminder.php
public function build()
{
$ics = $this->event->calendar_link->ics([], ['format' => 'file']);
return $this->subject('Event Reminder')
->attachData($ics, 'event.ics', ['mime' => 'text/calendar']);
}
Queue Jobs: Generate and send calendar links asynchronously:
// app/Jobs/SendCalendarInvite.php
public function handle()
{
$link = Link::create(...);
Mail::to($this->email)->send(new EventReminder($link));
}
Cache::remember):
$link = Cache::remember("event_{$event->id}_link", now()->addHours(1), function () use ($event) {
return Link::create(...)->google();
});
@if($showCalendarLinks)
<a href="{{ $event->calendar_link->google() }}">Add to Google</a>
@endif
public function test_google_link_generation()
{
$link = Link::create('Test', new \DateTimeImmutable('2023-12-01'), new \DateTimeImmutable('2023-12-01 12:00:00'));
$this->assertStringContainsString('action=TEMPLATE', $link->google());
}
public function test_calendar_link_in_blade()
{
$response = $this->get('/events/1');
$response->assertSee('href="https://calendar.google.com/...');
}
createAllDay uses inclusive days (e.g., 1 = single day). The package adds +1 day internally for calendar compatibility.
// Incorrect: Creates a 0-day event (empty)
Link::createAllDay('Event', $date, 0);
// Correct: Single-day event
Link::createAllDay('Event', $date, 1);
$link->timezone('Europe/Amsterdam');
$to is before $from:
try {
$link = Link::create('Event', $end, $start); // Throws InvalidLink
} catch (\InvalidArgumentException $e) {
// Handle error
}
How can I help you explore Laravel packages today?