mrshanebarron/calendar
mrshanebarron/calendar is a Laravel/PHP package for adding calendar functionality to applications. It provides a straightforward way to manage and display calendar events, making it easier to integrate scheduling features into Laravel projects.
The fastest path from install to a rendered month view:
composer require mrshanebarron/calendar is the entry point. Confirm your app's Laravel and PHP constraints against the package's composer.json before anything else — a mismatch surfaces here cheaply.sb- prefix (renamed in v1.0.1). Run php artisan vendor:publish and confirm exactly which tag and filename land in config/ — the release data confirms the prefix, not the exact tag string, so read the service provider if the tag isn't obvious.If you are upgrading from v1.0.0, stop and do the config migration first (see Gotchas) — do not assume the 1.0.1 patch bump is safe to roll out unattended.
Events are your data, the package is the renderer. The most maintainable integration treats the package as a presentation layer over models you already own. Keep event fetching in a repository, query object, or scoped Eloquent query, and feed the calendar a collection of objects that expose start and end. This keeps the scheduling logic — filtering by user, tenant, status — in your application, where it is testable, and leaves the package responsible only for layout.
One controller action per view, one view per action. A month view is the package's core deliverable. The pattern that scales:
MonthController@show resolves the target month from the request (defaulting to now), fetches the bounded event set, and renders a Blade view.Bound event queries to the visible range. Month views are a classic source of accidental full-table scans. Fetch only events whose range overlaps the visible month (typically where start <= month_end and end >= month_start). If the package exposes the rendered date range to your controller, use it as the source of truth for the query bounds so the two never drift.
Configure week start once, at the config layer. The week-start setting is environmental, not per-view. Set it in the published config, not in controller code, so every calendar in the app agrees and config caching behaves correctly.
Customize through published views, not vendor edits. The description calls out reusable Blade templates as a feature — the intended extension point is publishing those views into your app's resources/views and editing them there. Treat anything under vendor/ as read-only. If the package offers separate publish tags for config and views, publish them independently so you can upgrade one without clobbering the other.
Event output is a view concern. Styling and per-event rendering should happen in the Blade template, keyed on the event object you supplied. Keep the renderer ignorant of your domain: if you need different colors per event type, expose a type or color accessor on your event model and let the template branch on it, rather than subclassing package classes.
Testing. Assert on the resolved config and on rendered HTML output, not on internal class behavior. A feature test that hits the month route and asserts the presence of expected event titles and the previous/next URLs catches the majority of regressions — including the silent-config-fallback failure mode below.
The config rename is the one real hazard. v1.0.1 renamed the published config file to add the sb- prefix. If you already published under the old name, the file may be silently ignored and your app will boot on package defaults — no error, no warning, tests still green. Mitigate with an explicit assertion on a resolved config value in a smoke test or deploy step. The fix is: capture your customizations from the old file, re-publish under the new name, diff and re-apply, delete the stale file, then php artisan config:clear.
Config caching will hold the old filename. Any deployment running php artisan config:cache must rebuild the cache after upgrade, or production will keep reading the old config until the next cache cycle. Make config:cache part of every deploy pipeline, not an ad-hoc step.
Pin the 1.x version. The 1.0.1 release is a published-artifact rename shipped as a patch — a signal that the config filename may not yet be treated as part of the public contract. Use a tight constraint (~1.0.1 or similar) and review diffs on upgrade rather than floating ^1.0 unattended. Budget for one more contract adjustment through the 1.x line.
Don't hardcode the config path. Because the filename just moved once, reference config via config('...') keys rather than base_path('config/sb-...') strings, and your code survives the next rename.
Timezones are your problem, not the calendar's. Start/end dates stored in UTC and rendered in the user's timezone is almost never handled by a lightweight renderer. Decide explicitly whether you normalize to the app timezone before handing events to the view or whether you pass Carbon instances already localized. Boundary events — those that start before the month or end after it — are where this bug shows up first.
Week-start and range boundaries interact. If week start is Monday but the month begins on a Sunday, the "first row" of the grid includes a day from the previous month. Ensure your event query uses the rendered range, not the calendar month, or events in the leading/trailing cells will silently not display.
Empty states and large datasets. A month with zero events should render a real empty state, not a blank cell — handle this in your published view template. If a month can contain hundreds of events, decide now whether the renderer handles that or whether you need a "+N more" pattern in the template.
Debugging path when rendering looks wrong. In order: (1) dump the event collection your controller passes in — most "calendar is empty" bugs are query bugs; (2) dump the resolved config values; (3) confirm which Blade template actually rendered (check whether a published copy is shadowing the vendor one); (4) confirm config:cache isn't serving stale values.
Documentation is thin; the source is the spec. At 1.0.1 with one release note and no public changelog described, "read the service provider and the templates" is the practical support model. Keep your integration shallow — controller renders a view, view contains the component — so an upstream change costs you an afternoon, not a refactor.
How can I help you explore Laravel packages today?