spatie/icalendar-generator
Generate RFC 5545 iCalendar (.ics) feeds with a fluent PHP API. Create calendars and events (start/end times, summary, metadata) for use in Apple Calendar, Google Calendar, and other clients. Outputs valid iCalendar text ready to serve or download.
Installation:
composer require spatie/icalendar-generator
No additional configuration is required—just autoload the package.
First Use Case:
Generate a basic event and export it as an .ics file:
use Spatie\IcalendarGenerator\Components\Event;
use Spatie\IcalendarGenerator\Components\Calendar;
$event = Event::create()
->summary('Team Meeting')
->description('Weekly sync with the team')
->startsAt(\Carbon\Carbon::now())
->endsAt(\Carbon\Carbon::now()->addHours(1))
->location('Zoom');
$calendar = Calendar::create('Team Calendar', 'team@example.com')
->addEvent($event);
return response($calendar->render(), 200, [
'Content-Type' => 'text/calendar',
'Content-Disposition' => 'attachment; filename="meeting.ics"',
]);
Where to Look First:
tests/ directory for real-world usage patterns.Event Creation: Chain methods for clarity:
$event = Event::create()
->uid('unique-id-here') // Required for updates
->dtStamp(\Carbon\Carbon::now()) // Auto-generated if omitted
->organizer('user@example.com')
->attendees(['attendee1@example.com', 'attendee2@example.com'])
->addCustomProperty('X-MYAPP-COLOR', '#FF0000'); // Extensions
Recurring Events:
Use PeriodRule for recurrence:
use Spatie\IcalendarGenerator\Components\PeriodRule;
$recurrenceRule = PeriodRule::create()
->addFrequency('WEEKLY')
->addDayOfWeek('MO', 'WE', 'FR') // Monday, Wednesday, Friday
->until(\Carbon\Carbon::now()->addYear());
$event->recursAccordingTo($recurrenceRule);
Calendar Management:
$calendar = Calendar::create('My Calendar', 'admin@example.com');
$calendar->addEvent($event1)->addEvent($event2);
$event->timezone(\Carbon\Carbon::now()->timezone);
Exporting:
return response($calendar->render(), 200, [
'Content-Type' => 'text/calendar',
]);
return Storage::disk('public')->put('calendar.ics', $calendar->render());
return response()->streamDownload(
fn() => print($calendar->render()),
'meeting.ics'
);
Integration with Laravel:
Route::get('/calendar', function () {
$calendar = Calendar::create('API Calendar', 'api@example.com')
->addEvent(Event::create()->summary('API Event')->startsAt(now())->endsAt(now()->addHour()));
return response($calendar->render(), 200, [
'Content-Type' => 'text/calendar',
]);
});
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
class GenerateCalendarJob implements ShouldQueue
{
use Queueable;
public function handle()
{
$calendar = Calendar::create('Scheduled Calendar', 'jobs@example.com');
// ... add events
Storage::put('public/calendars/scheduled.ics', $calendar->render());
}
}
Custom Properties: Extend iCalendar with vendor-specific fields:
$event->addCustomProperty('X-MYAPP-CATEGORY', 'work')
->addCustomProperty('X-MYAPP-PRIORITY', 'high');
UID Uniqueness:
UID values break recurrence updates in clients (e.g., Google Calendar).$event->uid('event-' . Str::uuid()->toString());
Timezone Handling:
$event->timezone('America/New_York');
Carbon for consistency:
$event->startsAt(\Carbon\Carbon::now()->timezone('UTC'));
Recurrence Rules:
EXDATE, RDATE) may not render as expected.PeriodRule builder for common patterns:
$rule = PeriodRule::create()
->addFrequency('MONTHLY')
->addDayOfMonth(15); // 15th of every month
Character Encoding:
é, ñ) may corrupt the .ics file.return response($calendar->render(), 200, [
'Content-Type' => 'text/calendar; charset=utf-8',
]);
Large Calendars:
$calendar = Calendar::create('Large Calendar', 'admin@example.com');
foreach ($events as $event) {
$calendar->addEvent($event);
}
return response()->streamDownload(
fn() => print($calendar->render()),
'large_calendar.ics'
);
Client-Specific Quirks:
DTSTAMP if not set; may misinterpret RRULE with EXDATE.ORGANIZER for editable events.X-PROPERTIES; test thoroughly.Validate Output:
Use online validators like icalendar.org or tools like ical-buddy:
composer require vcal/ical-buddy
use Vcal\Ical\Parser;
$parser = new Parser();
$parser->parse($calendar->render());
Log Raw Output:
\Log::info('iCalendar Output:', ['raw' => $calendar->render()]);
Check for Deprecations: Monitor GitHub Releases for breaking changes.
Custom Components:
Extend the package by creating your own component (e.g., Todo):
namespace App\Icalendar;
use Spatie\IcalendarGenerator\Components\Component;
class Todo extends Component
{
public function summary(string $summary): self
{
$this->properties['SUMMARY'] = $summary;
return $this;
}
// ... other methods
}
Property Extensions: Add vendor-specific properties dynamically:
$event->addCustomProperty('X-MYAPP-TAGS', 'project:alpha,urgent');
Event Modifiers: Create a trait to standardize event creation:
trait StandardEvent
{
public function standardize(): Event
{
return $this->dtStamp(now())
->timezone(config('app.timezone'))
->organizer(auth()->user()->email);
}
}
Middleware for API: Add a middleware to enforce iCalendar headers:
namespace App\Http\Middleware;
use Closure;
class EnsureIcalendarHeaders
{
public function handle($request, Closure $next)
{
if ($request->wantsIcalendar()) {
$response = $next($request);
return $response->header('Content-Type', 'text/calendar');
}
How can I help you explore Laravel packages today?