Summary
Weekly availability / opening-hours editor with per-day toggles, from/to time slots, optional break entries, copy-to-weekdays, searchable timezone selector, and locked days. From/To use the shared FLEX Select control. Default control size issm; the timezone selector always renders at md.
Basic usage
State format
Day keys use three-letter English abbreviations:mon, tue, wed, thu, fri, sat, sun.
Times are HH:MM strings in 24-hour format (09:00, 17:30). On save, invalid slot entries are dropped and times are zero-padded.
Minimal example
Split shift with lunch break
Overnight slots
Add"overnight": true when a work slot crosses midnight (e.g. 22:00 → 06:00). The UI exposes an Overnight toggle on work slots; overlap validation uses the same ScheduleV2 / interval-engine rules as PHP.
timezone(null) hides the selector, omit the timezone key from state — it is not required or validated.
Export & audit helpers
Default state
The field default callsScheduleField::defaultSchedule():
Configuration API
All methods acceptClosure for dynamic configuration.
days(array|Closure $days)
Which days to render. Invalid day codes are ignored. Empty array falls back to all seven days.
Default: ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'].
timezone(string|Closure|null $timezone)
Default timezone identifier when the selector is shown, or null to hide the timezone block entirely.
Default: 'UTC' (selector visible).
md size regardless of the schedule field size().
timeStep(int|Closure $minutes)
Minute step for FlexTimeSegmentsField dropdowns (From / To). Clamped to 1–60.
Default: 5.
minSlots(int|Closure $count) / maxSlots(int|Closure $count)
Per-day slot limits (includes both work slots and breaks).
Defaults: 1 / 10.
requireSlotsForEnabledDays(bool|Closure $condition = true)
When true (default), each enabled day must have at least minSlots() valid entries.
When false, an enabled day may have zero slots (useful for “open but hours TBD” flows — use carefully).
allowCopyToWeekdays(bool|Closure $condition = true)
Shows Copy to weekdays on the copy source day.
Default: true.
copySourceDay(string|Closure $day = 'mon')
Which day row displays the copy button. Must be one of mon … sun.
Default: mon.
workdays(array|Closure $days)
Target days for Copy to weekdays. Invalid entries are filtered. Empty after filter falls back to Mon–Fri.
Default: ['mon', 'tue', 'wed', 'thu', 'fri'].
enabled flags.
lockedDays(array|Closure $days)
Days that cannot be toggled on/off. Shows a lock icon instead of the switch. Locked days still display their schedule when enabled in state.
Only days present in days() are kept.
variant(string|Closure $variant)
Visual container style.
size(string|ControlSize|Closure $size)
Size of time inputs and day rows. Default: sm.
Timezone selector remains md.
readOnly(bool|Closure $condition = true) / disabled(bool|Closure $condition = true)
Inherited from Filament. Disables toggles, time pickers, copy, and slot toolbar actions.
focusOutline(bool|Closure $condition = true)
Inherited from HasFieldFocusOutline. Default: false. Adds focus ring on time input shells when enabled.
Public helper methods
Validation
Built-in rule runs on submit (and when Livewire validates). Custom Filamentrequired is mapped to nullable at rule level — emptiness is checked inside the schedule validator when required() is set.
Real-time UI validation mirrors server messages via Alpine (
from_before_to, min_slots, max_slots, overlap).
Disabled days skip slot validation entirely.
Model & persistence
Recipe: restaurant — weekdays + lunch break
Recipe: support desk — weekdays only, no weekends in UI
Recipe: always-closed weekends (locked)
Recipe: no timezone selector (local hours)
Recipe: 30-minute steps, larger time inputs
Recipe: read-only preview on view page
Editing a schedule
Enable a day to reveal its periods. From and To use the FLEX Select control with your configured time step. Add working periods or breaks, reorder them, or remove periods while respecting your slot limits. Overnight periods can finish on the next day. Copying a day’s periods to your chosen workdays keeps their existing open/closed status.Responsive layout
The editor adapts to its own width. Inside a narrow card or on a phone, From and To use two equal columns with their labels above the controls. Large controls retain their size without forcing the card wider than its container. Text direction follows your page unless you setrtl() explicitly.
Related components
Table column
Use the same JSON attribute in a table. Only enabled days containing a working slot appear as circular badges. Hover a badge or focus it with the keyboard to see the day, all periods and timezone. A closed schedule shows “Closed” without inactive badges.Infolist entry
days() in the order you want, locale(), hourCycle(12|24) and showBreaks(false). Configuration values can be closures for per-record settings. Breaks appear with a separate label by default; a day containing only breaks does not count as open. Overnight periods include (+1) for the following day. Multiple periods are retained. Heading and description are optional and escaped as text. Use the array model cast for opening_hours; JSON strings and empty values are also handled.
The table and infolist are read-only presentations. Configure editing and validation with ScheduleField. They share the saved days/timezone structure. They do not infer holiday exceptions or perform availability/reservation checks.
Week order, language and text direction
UseweekStartsOn(0) for Sunday first or weekStartsOn(1) for Monday first. Without this option, days() preserves your chosen order. The option works on the field, table column and infolist entry. It changes presentation, not the stored day keys.
locale() changes the language of a display; it does not change the stored schedule. rtl() enables right-to-left layout, rtl(false) sets left-to-right, and rtl(null) inherits the page direction. Hour values and timezone identifiers remain readable in either direction.
Table tooltips are centered badges without an arrow. They wrap long schedules, stay within the screen width, and appear on keyboard focus as well as hover. Only enabled days containing working slots produce day circles.
Validation when saving
Use the normal Filament form save flow (getState()), which validates the submitted schedule before returning the value to save. Supported checks include required availability, timezone identifiers, missing days, hour and minute bounds, period order, per-day slot limits, overlapping periods, and overnight overlaps between adjacent days, including Sunday to Monday.
Submitted times must be hour/minute values such as 09:00 or 9:00. Relative expressions, numeric timestamps and arbitrary text are rejected. Slot data must be a list; a slot type is slot or break. Invalid flags and malformed retained slots on closed days are rejected too. Closed days do not require a period. requireSlotsForEnabledDays(false) makes the minimum optional for enabled days; maxSlots() still applies.
lockedDays() protects each selected day’s enabled/closed status both in the interface and during validation. Its time periods remain editable when the day is enabled. On edit, the protected status comes from the record’s attribute; on create, it comes from the field default.
For a custom fill, nested data or storage outside the record attribute, supply the original schedule from your trusted storage:
rules() API. Model authorization, preventing concurrent reservation conflicts and validating writes made outside the Filament form remain responsibilities of your application.