asantibanez/livewire-resource-time-grid
A Laravel Livewire component for displaying resources in a configurable time grid. Schedule and visualize events or reservations across days, weeks, or custom time slots, with model integration, date ranges, and customizable views for booking and resource-planning interfaces.
Install the package with Composer; there are no migrations or required asset builds:
composer require asantibanez/livewire-resource-time-grid
As of v1.6.0 the package supports Laravel 12 (alongside the earlier supported majors), so it can be used on a current Laravel app without a composer.json constraint workaround. The package is a single Livewire component. You either render it directly with arrays or, more commonly, extend its component class and supply the two data sources it needs: a list of resources (columns — rooms, technicians, machines) and a list of events (blocks placed on a resource at a time range for the displayed day). The fastest first use case is a "today" scheduling board: one column per room, one row per hour, bookings rendered as blocks.
namespace App\Livewire;
use App\Models\Booking;
use App\Models\Room;
use Asantibanez\LivewireResourceTimeGrid\LivewireResourceTimeGrid;
class RoomSchedule extends LivewireResourceTimeGrid
{
public function resources(): array
{
return Room::orderBy('name')->get()
->map(fn ($room) => ['id' => $room->id, 'title' => $room->name])
->all();
}
public function events(): array
{
return Booking::with('room')
->whereDate('starts_at', today())
->get()
->map(fn ($booking) => [
'id' => $booking->id,
'resourceId' => $booking->room_id,
'title' => $booking->title,
'start' => $booking->starts_at->toDateTimeString(),
'end' => $booking->ends_at->toDateTimeString(),
])
->all();
}
}
Then render it like any Livewire component (<livewire:room-schedule />). Where to look first: the package README for the exact prop names and event array shape in your installed version, then src/LivewireResourceTimeGrid.php and its Blade view — the source is small enough to read end-to-end in ten minutes, which is the best way to understand the click hooks (onEventClick, onTimeSlotClick, onResourceClick or their equivalents) and the hour-range/height configuration props. Wire one click hook to dd() before writing any UI.
Subclass for data, keep the parent for rendering. Override resources() and events() rather than passing big arrays from a parent component; it keeps the grid self-contained and lets Livewire re-query on each round trip. If your version supports props, passing :resources/:events is fine for read-only dashboards.
Treat the grid as a view, not a form. The typical workflow is: click an empty slot → open a modal prefilled with resourceId + time → save → the component re-renders and re-queries events(). Click an event → open an edit/delete modal. Keep the modal in the same component (or a child) so the grid refresh is automatic.
public function onTimeSlotClick($resourceId, $time): void
{
$this->dispatch('booking-create-requested', roomId: $resourceId, startsAt: $time);
}
public function onEventClick($eventId): void
{
$this->dispatch('booking-edit-requested', bookingId: $eventId);
}
Scope everything server-side. Filter resources by the authenticated user's permissions and events by the visible date in the query itself — never in Blade. The component receives IDs from the browser, so re-validate ownership/availability in the save handler, not just in the click callback.
Date navigation. For multi-day use, add a public $date property, use it in the events() query, and render one grid per selected day (with wire:key including the date). Avoid showing a week in one grid unless you've confirmed the package supports it; the component is built around a single day with resource columns.
Testing. The component is testable with Livewire::test(RoomSchedule::class); assert the rendered output contains event titles and that click hooks dispatch the expected events (assertDispatched on Livewire 3, assertEmitted on v2). Seed two resources and one overlapping booking to catch layout issues early.
Composition over inheritance. If you need the grid inside a larger dashboard, wrap it in a parent component and communicate via dispatched events rather than overriding every hook — this keeps the package upgrade surface small.
resources() and events() must return arrays of arrays with the exact keys the view expects. ->get() instead of ->all() is the most common silent failure.resourceId is an integer in events but a string in resources (or vice versa), blocks land in the wrong column or vanish. Cast consistently.events() constrained to the displayed day, eager-load relations used in the mapping, and avoid loading hundreds of bookings. If the grid feels slow, the query is the problem, not the component.content in tailwind.config.js or publish/override the Blade view. Publishing views is also the cleanest extension point for custom event colors or labels.composer.json constraints will block the install. Event dispatch APIs differ between Livewire 2 and 3, so after adding Laravel 12 support, confirm the resolved package version still matches your Livewire major and pin accordingly.composer.json, and be prepared to fork or override the Blade view if you need behavior the component doesn't expose — the codebase is small enough that forking is a realistic fallback rather than a last resort.How can I help you explore Laravel packages today?