korchid/calendar
korchid/calendar is a flexible calendar package for Laravel and PHP applications, offering a simple API to generate date grids, format dates, and render customizable calendar views for dashboards, schedulers, and booking interfaces. Lightweight and easy to integrate into existing projects.
orchid/calendar is a presentation-layer calendar builder for Laravel: it generates day/week/month/year grid markup (Blade views, Carbon-driven) that you embed in your own controller + view. It does not store events — you bring the data.
composer require orchid/calendar
Package discovery registers the service provider; there is normally nothing to add to config/app.php. If you need to change markup, publish the namespaced views (calendar::) into resources/views/vendor/calendar and edit there rather than forking.
Where to look first:
Calendar factory under Orchid\Calendar\) — this is what you type in your controller.View/ classes (Day, Week, Month, Year) — these hold the actual build logic and are where you go when the grid looks wrong.resources/views/ inside the package — the Blade templates that produce the markup; the fastest way to understand what data each granularity receives.First use case — drop a month grid onto a page:
use Orchid\Calendar\Calendar;
Route::get('/schedule', function () {
return view('schedule', [
'calendar' => Calendar::create()->months()->render(),
]);
});
Then in Blade, echo it raw (it is HTML, not escaped text):
{!! $calendar !!}
Note on version: v1.0.1 is a patch release against v1.0.0 with no documented API changes (the release notes only link the diff). Bumping within ^1.0 is safe; pin the constraint and re-read the diff before moving to 1.1.
Granularity is a choice, not a config. Pick the view type per screen — day/week for operational dashboards, month/year for overviews. Don't try to make one calendar serve both; build two and route between them.
Overlay your own events by date key. The package renders an empty grid; the work is mapping your Eloquent models onto dates. Query once for the visible range, key by Y-m-d, then pass the map into the view:
$start = Carbon::parse(request('month', 'now'))->startOfMonth()->startOfWeek();
$end = $start->copy()->endOfMonth()->endOfWeek();
$events = Event::query()
->whereBetween('starts_at', [$start, $end])
->get()
->groupBy(fn ($e) => $e->starts_at->toDateString());
return view('schedule', [
'calendar' => Calendar::create()->months()->render(),
'events' => $events,
]);
Drive navigation from the query string. ?month=2024-05 (or ?date=…) parsed with Carbon::parse() for the anchor, plus prev/next links built from copy()->subMonthNoOverflow(). Keeps the calendar stateless and shareable via URL.
Embed in Orchid Screens like any other block. Inside a screen's layout(), wrap the rendered HTML in a Layout/Wrapper (or a Modal/Rows element) rather than a field — it's read-only output, so Field types are the wrong abstraction.
Cache static or rarely-changing grids. The rendered string is deterministic for a given anchor date + locale + week start. Cache::remember("cal:{$anchor}:{$locale}", now()->addHour(), fn () => ...) removes the cost on high-traffic dashboards; bust on event write.
Freeze time in tests. Carbon::setTestNow() before rendering and assert on the produced markup; this is the only reliable way to keep grid assertions stable across months.
{!! !!}. Using {{ }} silently produces escaped tags and a "blank" calendar — a very common first-day bug.App::setLocale() / Carbon::setLocale()) before rendering, and re-render per locale rather than caching one locale's output under a shared key.config('app.timezone'). If your stored timestamps are UTC but the UI is local, convert before grouping into date buckets — otherwise events land on the wrong day near midnight.subMonth() vs subMonthNoOverflow() — anchoring on the 31st will skip months.startOfMonth..endOfMonth loses the leading/trailing days the grid still renders. Always expand to startOfWeek/endOfWeek of the anchor.View/* class. Avoid editing the vendor directory; the package namespace views support publishing precisely so you don't have to.How can I help you explore Laravel packages today?