Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Ical Bundle Laravel Package

welp/ical-bundle

Symfony bundle for working with iCalendar (ICS) files: parse and generate .ics calendars and events, and integrate calendar data into your Symfony applications.

View on GitHub
Deep Wiki
Context7

Getting Started

The package is a Symfony bundle, not a Laravel package, so there is no IcalServiceProvider or facade to lean on. Plan to spend the first hour reading src/ and Resources/config/services.yaml rather than the README — the bundle's behaviour lives almost entirely in those two places.

  1. Install with composer require welp/ical-bundle. Because it targets an older Symfony (pre-5.x era, last tagged 2020-03), expect Composer to complain about symfony/* version constraints. You will almost certainly need to add --with-all-dependencies or pin the underlying iCal library version that the bundle wraps.
  2. Skip the Bundle registration. Laravel has no bundles.php. Instead, register the bundle's services manually in a dedicated service provider (see Implementation Patterns), or — the pragmatic route — bypass the bundle entirely and depend on whatever iCal library it pulls in (typically an eluceo/ical-style builder) and use it directly. Given 10 stars and zero dependents, the bundle's wrapper adds little value over the library it decorates.
  3. First use case to target: generating an .ics payload for a single calendar event and returning it as a download response. This exercises serialisation, timezone handling, and the response path in one go:
$event = [
    'start'    => new \DateTimeImmutable('2026-06-01 10:00', new \DateTimeZone('Europe/Berlin')),
    'end'      => new \DateTimeImmutable('2026-06-01 11:00', new \DateTimeZone('Europe/Berlin')),
    'summary'  => 'Kickoff',
    'location' => 'Room 4',
];

return response($icalService->render($event), 200, [
    'Content-Type'        => 'text/calendar; charset=utf-8',
    'Content-Disposition' => 'attachment; filename="event.ics"',
]);

Implementation Patterns

Bridge the bundle's services into the Laravel container

If you want the bundle's builder rather than the raw library, register its services in a provider and expose a facade:

public function register(): void
{
    $this->app->singleton(\Welp\IcalBundle\Service\IcalService::class, function ($app) {
        return new \Welp\IcalBundle\Service\IcalService(
            // whatever the bundle's services.yaml passes in — read it first
        );
    });
}

Bind it as a singleton so repeated event additions within one request accumulate into a single VCALENDAR body. A fresh instance per event produces multiple VCALENDAR blocks glued together, which most calendar clients reject.

Feed endpoint pattern

The most natural day-to-day integration is a subscribable feed, not a one-off download. Model it as a controller action that queries Eloquent, maps rows to events, and streams the result:

public function feed(User $user): Response
{
    $events = $user->events()->where('starts_at', '>', now())->get();

    return response($this->ical->renderCollection($events), 200, [
        'Content-Type' => 'text/calendar; charset=utf-8',
    ])->setCache(['public' => 60, 'max_age' => 600]);
}

Apple Calendar and Google Calendar poll these URLs; caching headers matter more than you'd think, and conditional GETs (ETag/Last-Modified) cut load noticeably.

Email attachments

For invitations, generate the payload and attach it via Laravel's mailer:

$mail->attachData($ical->render($event), 'invite.ics', ['mime' => 'text/calendar']);

Set the METHOD:REQUEST property (if the underlying library exposes it) so Outlook treats it as a meeting invite rather than a file. Without it, invitations show as plain attachments.

Event DTO mapping

Keep Eloquent models out of the calendar builder. Map them into a small CalendarEvent value object (start, end, UID, summary, description, location, status, sequence). This gives you one place to enforce the two rules that bite everyone: UID stability (derive from "event-{$model->id}@yourdomain", never regenerate per request) and SEQUENCE increments on updates so subscribed clients refresh rather than ignore.

Gotchas and Tips

  • Abandonment risk is real. Last release March 2020, single-digit stars, zero dependents. Audit whether it still installs against your PHP/Symfony versions before committing. If it doesn't, fork the two or three classes you actually use — the surface area is small — or switch to a maintained generator (spatie/icalendar-generator, eluceo/ical) and save yourself the bridge work.
  • Symfony DI assumptions. The bundle's services live in Resources/config/services.yaml and may reference Symfony-only services (RequestStack, EventDispatcherInterface, translator). Anything it depends on that Laravel doesn't bind will throw at resolution time. Read the YAML before registering anything.
  • Timezones. DateTime objects without an explicit DateTimeZone inherit PHP's default, which may differ from your app timezone. Always construct with an explicit zone, and if events are in floating/local time, ensure the library emits DTSTART;TZID=... rather than a UTC Z value. A one-hour drift in a feed is almost always this.
  • RFC 5545 line folding. Lines must be CRLF-terminated and folded at 75 octets. Some older builders just \r\n the content and don't fold long DESCRIPTION fields, producing files that parse in one client but not another. If you hand-roll anything, test with a long description.
  • Escaping. Commas, semicolons, backslashes, and newlines in SUMMARY/DESCRIPTION must be escaped (\,, \;, \\, \n). User-entered event titles are the usual source of corrupt .ics files.
  • Config quirks. Symfony bundle config (ical: { ... }) has no Laravel equivalent. If the bundle reads kernel parameters or %env()% placeholders, replicate those via config/services.php and your own Config access, or hardcode values in the bridge provider.
  • Streaming vs. building in memory. Large feeds (hundreds of events) are fine in memory, but the builder holds the whole VCALENDAR string. For thousands, chunk and echo with ob_flush() on a StreamedResponse.
  • Extension points are thin. There's no documented event system or custom-property hook. If you need X--prefixed properties or VALARM blocks, you'll subclass the builder or post-process the serialized output with regex — not elegant, so wrap it in a small adapter you control so you can swap the library later.
  • Debugging tip: validate generated output against icalendar.org's validator before blaming the calendar client. Nine times out of ten the malformity is line endings or an unescaped character, not the client.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels
wazza/laravel-db-encryption
hokny/laravel-generator
vonsogt/backup
jengo/queues
jeremykenedy/laravel-observability
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums