Scheduling looks simple until you actually have to build it.
At first, the requirement usually sounds straightforward:
βLet the user choose a date and time.β
A standard date-time picker handles that perfectly.
But real scheduling workflows rarely stop there.
What if users should only book during working hours?
What if appointments are available every 30 minutes?
What if some slots are already booked?
What if a reservation must be made at least two hours in advance?
What if availability comes dynamically from your database or an external API?
And what happens when your users and your application operate in different timezones?
At that point, choosing a date and time is no longer just a date-picker problem. It becomes a time-slot scheduling problem.
That is the problem I wanted to solve with Filament Date Time Slots.
A scheduling field, not another booking system
Filament Date Time Slots is an open-source form field for Filament designed specifically around date and time-slot selection.
The goal is intentionally simple:
Provide the scheduling UI and rules without forcing a booking architecture on your application.
The package doesn't require an appointments table, a reservation model, or a specific database schema.
At the end of the interaction, your Filament form still receives a normal datetime value.
That makes the field useful in many different workflows:
- Appointments
- Reservations
- Meetings
- Tasks
- CRM follow-ups
- Consultations
- Service bookings
- Delivery windows
- Any workflow where users need to choose an available time
Installation
Install the package through Composer:
composer require wooserv/filament-date-time-slots
Then use the field in your Filament schema:
use WooServ\FilamentDateTimeSlots\Forms\Components\DateTimeSlotPicker;
DateTimeSlotPicker::make('scheduled_at')
That's enough to get started.
The package ships with its compiled frontend assets, so applications using it don't need to install its JavaScript dependencies or run a separate frontend build process.
Defining working hours
One of the most common scheduling requirements is limiting availability to specific hours.
Instead of letting users choose arbitrary times, you can define the working schedule and let the field generate the available slots.
For example:
DateTimeSlotPicker::make('scheduled_at')
->workingHours([
1 => ['09:00', '17:00'],
2 => ['09:00', '17:00'],
3 => ['09:00', '17:00'],
4 => ['09:00', '17:00'],
5 => ['09:00', '17:00'],
])
->slotInterval(30)
Now the UI can present 30-minute slots within the configured working hours instead of exposing every possible time.
This is much closer to how scheduling works in real applications.
When availability comes from your application
Generating slots from working hours is useful, but sometimes your backend already knows exactly which slots are available.
Maybe availability depends on:
- A doctor's calendar
- An employee's schedule
- Existing reservations
- A resource or room
- Business rules
- An external scheduling service
In those cases, you can provide available slots directly:
DateTimeSlotPicker::make('scheduled_at')
->availableSlots([
'09:00',
'09:30',
'11:00',
'14:30',
])
More importantly, scheduling data doesn't have to be static.
The package supports dynamic configuration, allowing availability to be calculated from your application's own data and logic.
That was an important design decision for me.
A scheduling component shouldn't try to own your availability logic. Your application should.
The field should simply provide a clean way to represent that availability to the user.
Handling booked or unavailable slots
Available times are only half of the problem.
Real systems also need to represent times that can no longer be selected.
For that, the field supports blocked slots:
DateTimeSlotPicker::make('scheduled_at')
->blockedSlots([
'10:00',
'10:30',
'15:00',
])
This can be useful when existing bookings already occupy part of the schedule.
Depending on the experience you want, blocked slots can either disappear from the available choices or remain visible as disabled slots.
Keeping them visible can be especially useful in appointment systems because users can understand that a time exists but is no longer available.
Booking constraints
Scheduling usually includes rules beyond availability.
For example, imagine a consultation that cannot be booked less than two hours before it starts.
The field supports a minimum lead time:
DateTimeSlotPicker::make('scheduled_at')
->minimumLeadTime(120)
You can also control the allowed date range and disable specific dates.
These constraints let the form communicate the same rules that your scheduling workflow expects instead of relying on users to choose a valid datetime manually.
Preventing past dates and times
A surprisingly common issue with generic date-time fields is dealing with the current day.
Disabling yesterday is easy.
But if today is selectable, the application also needs to make sure that time slots that have already passed are no longer available.
A scheduling-focused field needs to understand both sides of that problem:
the selected date and the available time slots for that date.
This is one of the reasons I preferred building a dedicated scheduling field rather than adding more configuration around a generic datetime input.
Timezones matter
Timezone handling is another area where scheduling interfaces can become complicated quickly.
The package supports configuring the timezone used by the picker while allowing the application to keep its own storage timezone.
For example:
DateTimeSlotPicker::make('scheduled_at')
->timezone('Asia/Riyadh')
This is useful when your application's stored datetime and the timezone shown to the person making the booking aren't necessarily the same.
Timezone support is part of the field's datetime lifecycle rather than simply changing a label in the interface.
Localization, RTL, and time formats
Scheduling interfaces are user-facing components, so localization matters.
The picker supports locale configuration, RTL layouts, and both 12-hour and 24-hour time display.
For example:
DateTimeSlotPicker::make('scheduled_at')
->locale('ar')
->rtl()
->timeFormat('12')
This was particularly important for applications that need to work well across both Arabic and English interfaces.
Dark mode is supported as well, so the field can fit naturally into Filament panels using either theme.
Why return a normal datetime?
One of the design choices I like most about this approach is what the package doesn't do.
It doesn't introduce its own booking model.
It doesn't create scheduling tables.
It doesn't dictate how appointments should be stored.
It doesn't try to become a calendar platform.
Instead:
DateTimeSlotPicker::make('scheduled_at')
still represents a normal field in your form.
Your application remains responsible for its business model.
That means the same component can be used in a CRM today and an appointment system tomorrow without changing the architecture around it.
For reusable packages, I think that separation is valuable.
Compatibility
The package is designed to work across the current Filament ecosystem, including:
- Filament 3, 4, and 5
- Laravel 11, 12, and 13
- PHP 8.2+
Compatibility is continuously tested through GitHub Actions across representative supported combinations.
That was important because supporting several framework generations shouldn't just mean adding broad Composer constraints.
The combinations should actually be tested.
Open source
Filament Date Time Slots is open source and released under the MIT license.
You can find the source code, documentation, examples, and issue tracker on GitHub:
GitHub:
https://github.com/wooserv/filament-date-time-slots
Packagist:
https://packagist.org/packages/wooserv/filament-date-time-slots
Install it with:
composer require wooserv/filament-date-time-slots
If you're building scheduling workflows with Filament, I'd love to hear what kinds of availability rules you're dealing with.
Issues, feedback, and contributions are welcome.
Top comments (0)