PHPackages                             el-schneider/statamic-calendar - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. el-schneider/statamic-calendar

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

el-schneider/statamic-calendar
==============================

Recurring events and cached occurrences for Statamic — with RRULE support, Antlers tags, month grid, REST API, and iCal export.

v0.1.0(3mo ago)024[5 issues](https://github.com/el-schneider/statamic-calendar/issues)[1 PRs](https://github.com/el-schneider/statamic-calendar/pulls)PHPCI passing

Since Feb 8Pushed 2mo agoCompare

[ Source](https://github.com/el-schneider/statamic-calendar)[ Packagist](https://packagist.org/packages/el-schneider/statamic-calendar)[ RSS](/packages/el-schneider-statamic-calendar/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (1)Dependencies (6)Versions (4)Used By (0)

[![Statamic Calendar](images/ca_banner.png)](images/ca_banner.png)

Statamic Calendar
=================

[](#statamic-calendar)

Recurring events and cached occurrences for Statamic. Works with Statamic 5 and 6.

Features
--------

[](#features)

- Define complex recurrence patterns using RFC 5545 (RRULE) via `rlanvin/php-rrule`
- Materialize occurrences into Laravel cache for fast listings
- Antlers tags for listing, current occurrence, next occurrences, and month grid
- Month calendar view — server-rendered, navigable via query params, no JS required
- JSON REST API for JS-based calendar components (opt-in)
- iCalendar (.ics) feed for calendar app subscriptions + per-event "Add to calendar" downloads
- Two URL strategies: query string (default, Statamic-native) or date segments
- Example templates for index (list, archive, calendar grid) and show pages

Requirements
------------

[](#requirements)

- PHP 8.3+
- Statamic 5 or 6
- Laravel 11+

Installation
------------

[](#installation)

```
composer require el-schneider/statamic-calendar
```

Publish the config:

```
php artisan vendor:publish --tag=statamic-calendar
```

### Blueprint Setup

[](#blueprint-setup)

The addon expects a `dates` grid field on your event entries. You can publish an example blueprint to get started:

```
php artisan vendor:publish --tag=statamic-calendar-examples
```

This publishes to `resources/vendor/statamic-calendar/examples/`. Copy the blueprint to your collection:

```
cp resources/vendor/statamic-calendar/examples/blueprints/collections/events/event.yaml \
   resources/blueprints/collections/events/event.yaml
```

Or add the `dates` grid field to your existing blueprint manually. See the example blueprint for the expected sub-field handles (`start_date`, `start_time`, `end_date`, `end_time`, `is_all_day`, `is_recurring`, `frequency`, etc.).

### Build the Cache

[](#build-the-cache)

After adding events, rebuild the occurrence cache:

```
php artisan occurrences:rebuild
```

The cache rebuilds automatically when entries are saved or deleted in the Control Panel.

URL Strategies
--------------

[](#url-strategies)

The addon supports two strategies for occurrence URLs. Configure in `config/statamic-calendar.php`:

### Query String (default)

[](#query-string-default)

Uses Statamic's native collection routing. The addon doesn't register any routes — your collection config handles everything:

```
# content/collections/events.yaml
route: '/events/{slug}'
```

Occurrence URLs look like `/events/my-event?date=2025-03-15`.

### Date Segments (opt-in)

[](#date-segments-opt-in)

For SEO-friendly date-based URLs like `/calendar/2025/03/15/my-event`. Enable in config:

```
'url' => [
    'strategy' => 'date_segments',
    'date_segments' => [
        'prefix' => 'calendar',
    ],
],
```

The addon registers a route at `/{prefix}/{year}/{month}/{day}/{slug}`.

iCalendar (.ics) Export
-----------------------

[](#icalendar-ics-export)

The addon exposes an .ics feed that calendar apps (Apple Calendar, Google Calendar, Outlook) can subscribe to. Enabled by default.

### Subscribable Feed

[](#subscribable-feed)

`GET /calendar.ics` — returns all cached occurrences as a standard iCalendar feed. Calendar apps can subscribe to this URL and will receive updates as events change.

### Single-Event Download

[](#single-event-download)

`GET /calendar.ics/{occurrenceId}` — returns a single occurrence as a downloadable .ics file. Use this for "Add to calendar" buttons. The response includes a `Content-Disposition: attachment` header.

### Configuration

[](#configuration)

```
// config/statamic-calendar.php
'ics' => [
    'enabled' => true,           // set to false to disable .ics routes
    'feed_url' => '/calendar.ics',
    'calendar_name' => env('APP_NAME', 'Calendar'),
],
```

### Template Usage

[](#template-usage)

```
{{-- Subscribe link --}}
Subscribe to calendar

{{-- Per-event download inside a calendar loop --}}
{{ calendar from="now" limit="10" }}

    Add to calendar

{{ /calendar }}
```

REST API
--------

[](#rest-api)

An opt-in JSON API for occurrences, designed for JS-based calendar components (FullCalendar, Toast UI Calendar, custom Alpine/Vue/React widgets, etc.) that build their view client-side.

### Enable

[](#enable)

Set the env var or publish the config:

```
STATAMIC_CALENDAR_API_ENABLED=true
```

```
// config/statamic-calendar.php
'api' => [
    'enabled' => env('STATAMIC_CALENDAR_API_ENABLED', false),
    'route' => env('STATAMIC_CALENDAR_API_ROUTE', 'api/calendar/occurrences'),
    'middleware' => env('STATAMIC_CALENDAR_API_MIDDLEWARE', 'api'),
],
```

### Endpoint

[](#endpoint)

`GET /api/calendar/occurrences`

ParameterTypeDescriptionDefault`from``date`Start date (ISO 8601 or `Y-m-d`)now`to``date`End date—`limit``int`Max occurrences—`sort``string``asc` or `desc``asc``tags``string`Comma-separated tag slugs—`organizer``string`Organizer entry ID—### Response

[](#response)

```
{
  "data": [
    {
      "id": "abc-123-2026-03-06-150000",
      "entry_id": "abc-123",
      "title": "Laracon Online",
      "slug": "laracon-online",
      "teaser": "The best Laravel conference",
      "organizer_id": "org-456",
      "organizer_slug": "laravel-org",
      "organizer_title": "Laravel",
      "organizer_url": "/organizers/laravel-org",
      "tags": ["tech", "laravel"],
      "start": "2026-03-06T15:00:00+00:00",
      "end": "2026-03-06T16:00:00+00:00",
      "is_all_day": false,
      "is_recurring": true,
      "recurrence_description": "every week on Friday",
      "url": "/events/laracon-online?date=2026-03-06"
    }
  ]
}
```

### Examples

[](#examples)

```
// Month view
fetch('/api/calendar/occurrences?from=2026-03-01&to=2026-03-31')

// Week view
fetch('/api/calendar/occurrences?from=2026-03-02&to=2026-03-08')

// Next 5 upcoming
fetch('/api/calendar/occurrences?limit=5')

// Filtered by tag and organizer
fetch('/api/calendar/occurrences?tags=music,art&organizer=org-123')
```

### CORS

[](#cors)

The API uses Laravel's `api` middleware group, so cross-origin requests are handled by your app's `config/cors.php`. Laravel's default config already allows `api/*` paths from all origins — adjust as needed.

Setting Up Templates
--------------------

[](#setting-up-templates)

### Events Index

[](#events-index)

Create a page or route that uses the `{{ calendar }}` tag. For example, add to `routes/web.php`:

```
Route::statamic('events', 'events/index', [
    'title' => 'Upcoming Events',
]);
```

Then create `resources/views/events/index.antlers.html`:

```
Upcoming Events

{{ calendar from="now" limit="20" }}

    {{ title }}
    {{ start format="l, M j, Y" }}
    {{ if is_recurring }}
      {{ recurrence_description }}
    {{ /if }}

{{ /calendar }}
```

### Event Show Page

[](#event-show-page)

Set the collection template to `events/show`, then create `resources/views/events/show.antlers.html`:

```
{{ title }}

{{ calendar:current_occurrence }}
  {{ start format="l, F j, Y" }}
  {{ if !is_all_day }}

      {{ start format="g:i A" }}
      {{ if end }}– {{ end format="g:i A" }}{{ /if }}

  {{ else }}
    All day
  {{ /if }}
  {{ if is_recurring }}
    Repeats: {{ recurrence_description }}
  {{ /if }}
{{ /calendar:current_occurrence }}
{{ calendar:next_occurrences :entry="id" limit="5" }}
  {{ start format="M j, Y" }}
{{ /calendar:next_occurrences }}
```

The `{{ calendar:current_occurrence }}` tag reads the `?date=` query parameter and resolves the matching occurrence for the current entry. When using the `date_segments` strategy, the date is extracted from the URL instead.

Antlers Tags
------------

[](#antlers-tags)

### `{{ calendar }}`

[](#-calendar-)

Lists occurrences from the cache (or resolves them live for non-default collections).

ParameterDescriptionDefault`from`Start date`now``to`End date—`limit`Max occurrences—`collection`Collection handleconfig value`tags`Filter by taxonomy terms—### `{{ calendar:month }}`

[](#-calendarmonth-)

Renders a month grid with weeks, days, and occurrences. Navigation via query params, fully server-rendered. See the example index template for usage.

ParameterDescriptionDefault`param`Query string parameter name (allows multiple calendars per page)`month``week_starts_on`Day of week (`0` = Sunday, `1` = Monday)`1``fixed_rows`Always render 6 rows for consistent grid height`false``collection`Collection handleconfig value`tags`Filter by taxonomy terms—Variables available inside the tag pair: `month_label`, `year`, `month`, `prev_url`, `next_url`, `today`, `day_labels` (loop with `label`, `full_label`), and `weeks` → `days` → `date`, `day`, `is_current_month`, `is_today`, `occurrences`.

### `{{ calendar:current_occurrence }}`

[](#-calendarcurrent_occurrence-)

Resolves the current occurrence for the entry in context, based on the `?date=` query param. Use as a tag pair — variables available inside:

- `start` — Carbon date
- `end` — Carbon date (nullable)
- `is_all_day` — boolean
- `is_recurring` — boolean
- `recurrence_description` — human-readable recurrence rule
- `occurrence_url` — the occurrence URL

### `{{ calendar:next_occurrences }}`

[](#-calendarnext_occurrences-)

Lists upcoming occurrences for a specific entry.

ParameterDescriptionDefault`entry`Entry IDcurrent context `id``from`Start date`now``to`End date—`limit`Max occurrences`5`### `{{ calendar:ics_url }}`

[](#-calendarics_url-)

Returns the URL to the .ics calendar feed. Use it to offer a "Subscribe" link:

```
Subscribe to calendar
```

### `{{ calendar:ics_download_url }}`

[](#-calendarics_download_url-)

Returns the .ics download URL for a single occurrence. Use inside any `{{ calendar }}` loop to offer an "Add to calendar" button:

```
{{ calendar from="now" limit="10" }}
  {{ title }}
  Add to calendar
{{ /calendar }}
```

ParameterDescriptionDefault`occurrence_id`Occurrence ID (`{entry_id}-{Y-m-d-His}`)current context `id`### `{{ calendar:for_organizer }}`

[](#-calendarfor_organizer-)

Lists upcoming occurrences for an organizer (from cache).

ParameterDescriptionDefault`organizer`Organizer entry IDcurrent context `id``limit`Max occurrences`5`Configuration
-------------

[](#configuration-1)

Key options in `config/statamic-calendar.php`:

KeyDescriptionDefault`collection`Source collection handle`events``fields.dates`Grid field handle`dates``url.strategy``query_string` or `date_segments``query_string``url.query_string.param`Query parameter name`date``url.date_segments.prefix`URL prefix for date segments`calendar``api.enabled`Enable JSON REST API`false``api.route`API route path`api/calendar/occurrences``api.middleware`Middleware group`api``ics.enabled`Enable .ics feed routes`true``ics.feed_url`Feed URL path`/calendar.ics``ics.calendar_name`Calendar name in .ics output`APP_NAME``cache.key`Cache store key`statamic_calendar.occurrences``cache.days_ahead`Recurrence expansion window`365`Cache
-----

[](#cache)

Occurrences are materialized into Laravel's cache for fast listing. The cache rebuilds automatically when entries are saved or deleted.

Manual rebuild:

```
php artisan occurrences:rebuild
```

Example Blueprint
-----------------

[](#example-blueprint)

Publish the example blueprint:

```
php artisan vendor:publish --tag=statamic-calendar-examples
```

Then copy it into your collection's blueprints directory (see [Blueprint Setup](#blueprint-setup) above).

Testing
-------

[](#testing)

```
composer install
vendor/bin/pest
```

Contributing
------------

[](#contributing)

Contributions are welcome! Please open an issue first to discuss what you'd like to change.

License
-------

[](#license)

MIT — see [LICENSE.md](LICENSE.md) for details.

###  Health Score

33

—

LowBetter than 75% of packages

Maintenance85

Actively maintained with recent releases

Popularity9

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity26

Early-stage or recently created project

 Bus Factor1

Top contributor holds 89.5% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

91d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/81a231c7730414820ac67caf7b30bea0de0d6af3775ec941a07ee5dc697f442a?d=identicon)[mail@jonaslist.de](/maintainers/mail@jonaslist.de)

---

Top Contributors

[![el-schneider](https://avatars.githubusercontent.com/u/26460248?v=4)](https://github.com/el-schneider "el-schneider (34 commits)")[![ito-ito-ito-ito](https://avatars.githubusercontent.com/u/260886217?v=4)](https://github.com/ito-ito-ito-ito "ito-ito-ito-ito (4 commits)")

---

Tags

calendaricallaravelphprecurring-eventsrrulestatamicstatamic-addon

###  Code Quality

TestsPest

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/el-schneider-statamic-calendar/health.svg)

```
[![Health](https://phpackages.com/badges/el-schneider-statamic-calendar/health.svg)](https://phpackages.com/packages/el-schneider-statamic-calendar)
```

###  Alternatives

[statamic/seo-pro

65440.7k](/packages/statamic-seo-pro)[jacksleight/statamic-bard-texstyle

17172.5k](/packages/jacksleight-statamic-bard-texstyle)[marcorieser/statamic-livewire

A Laravel Livewire integration for Statamic.

2381.5k10](/packages/marcorieser-statamic-livewire)[withcandour/aardvark-seo

Save time and get your Statamic site to rank better with the SEO addon for Statamic.

13128.3k](/packages/withcandour-aardvark-seo)[alt-design/alt-sitemap

Alt Sitemap addon, create a sitemap from Statamic entries

1219.0k](/packages/alt-design-alt-sitemap)[studio1902/statamic-peak-tools

11146.7k](/packages/studio1902-statamic-peak-tools)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
