el-schneider/statamic-calendar

There is no license information available for the latest version (v0.1.0) of this package.

Installs: 0

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 3

pkg:composer/el-schneider/statamic-calendar

v0.1.0 2026-02-08 17:03 UTC

This package is auto-updated.

Last update: 2026-02-08 17:21:44 UTC


README

Statamic Calendar

Statamic Calendar

Recurring events and cached occurrences for Statamic.

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

Installation

composer require el-schneider/statamic-calendar

Publish the config:

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

URL Strategies

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

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)

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

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

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

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

// 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

{{-- Subscribe link --}}
<a href="{{ calendar:ics_url }}">Subscribe to calendar</a>

{{-- Per-event download inside a calendar loop --}}
{{ calendar from="now" limit="10" }}
  <a href="{{ calendar:ics_download_url :occurrence_id="id" }}">
    Add to calendar
  </a>
{{ /calendar }}

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

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

GET /api/calendar/occurrences

Parameter Type Description Default
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

{
  "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

// 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

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

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:

<h1>Upcoming Events</h1>

{{ calendar from="now" limit="20" }}
  <a href="{{ url }}">
    <h2>{{ title }}</h2>
    <p>{{ start format="l, M j, Y" }}</p>
    {{ if is_recurring }}
      <p>{{ recurrence_description }}</p>
    {{ /if }}
  </a>
{{ /calendar }}

Event Show Page

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

<h1>{{ title }}</h1>

{{ calendar:current_occurrence }}
  <p>{{ start format="l, F j, Y" }}</p>
  {{ if !is_all_day }}
    <p>
      {{ start format="g:i A" }}
      {{ if end }}– {{ end format="g:i A" }}{{ /if }}
    </p>
  {{ else }}
    <p>All day</p>
  {{ /if }}
  {{ if is_recurring }}
    <p>Repeats: {{ recurrence_description }}</p>
  {{ /if }}
{{ /calendar:current_occurrence }}
{{ calendar:next_occurrences :entry="id" limit="5" }}
  <a href="{{ url }}">{{ start format="M j, Y" }}</a>
{{ /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

{{ calendar }}

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

Parameter Description Default
from Start date now
to End date
limit Max occurrences
collection Collection handle config value
tags Filter by taxonomy terms

{{ calendar:month }}

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

Parameter Description Default
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 handle config 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 weeksdaysdate, day, is_current_month, is_today, occurrences.

{{ calendar:current_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 }}

Lists upcoming occurrences for a specific entry.

Parameter Description Default
entry Entry ID current context id
from Start date now
to End date
limit Max occurrences 5

{{ calendar:ics_url }}

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

<a href="{{ calendar:ics_url }}">Subscribe to calendar</a>

{{ calendar:ics_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" }}
  <h2>{{ title }}</h2>
  <a href="{{ calendar:ics_download_url :occurrence_id="id" }}">Add to calendar</a>
{{ /calendar }}
Parameter Description Default
occurrence_id Occurrence ID ({entry_id}-{Y-m-d-His}) current context id

{{ calendar:for_organizer }}

Lists upcoming occurrences for an organizer (from cache).

Parameter Description Default
organizer Organizer entry ID current context id
limit Max occurrences 5

Configuration

Key options in config/statamic-calendar.php:

Key Description Default
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

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

Publish the example blueprint:

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