> ## Documentation Index
> Fetch the complete documentation index at: https://flex-fields.bjanczak.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Examples target Filament 5. Verify optional dependencies before configuring integrations.
> Upload transfer completion, permanent storage and record acceptance are separate milestones. Native and Spatie image conversions retain the original.

# Date range picker

> Configure range shortcuts, one/two calendars, local times and strict endpoint validation.

<video autoPlay muted loop playsInline controls preload="metadata" src="https://mintcdn.com/flex-fields/DK_1kkuwd6AOeCzB/art/previews/date-range-picker-motion.mp4?fit=max&auto=format&n=DK_1kkuwd6AOeCzB&q=85&s=caaf87b8f664821921b548624d2d88ef" poster="/art/previews/date-range-picker.webp" width="1920" height="1080" aria-label="Date range picker — FLEX Fields demonstration">
  [Watch the Date range picker demonstration (MP4).](https://flex-fields.bjanczak.com/art/previews/date-range-picker-motion.mp4)
</video>

`FlexDateRangeField` stores an ordered pair of dates, optionally with local times. The panel maintains a draft until Apply; Cancel, Escape and an outside click discard that draft.

## Basic period

```php theme={"theme":{"light":"github-light","dark":"github-dark"}}
use Bjanczak\FilamentFlexFields\Filament\Forms\Components\FlexDateRangeField;

FlexDateRangeField::make('period')
    ->label('Report period')
    ->default(['start' => '2026-10-10', 'end' => '2026-10-15']);
```

The saved value is an array with `start` and `end`. An empty field saves `null`. The default day-only English label condenses dates, for example `Oct 10 – 15, 2026`. Custom formats and editable fields show both complete endpoints.

## Default shortcuts

Without `rangePresets()`, the panel offers Today, Yesterday, Last 7 days, Last 30 days, This month, Last month, This quarter and Year to date. Last 7/30 days include Today; This month/quarter end at Today. Last month includes that entire previous month. These reporting periods use Gregorian dates.

## Remove the shortcut panel

```php theme={"theme":{"light":"github-light","dark":"github-dark"}}
FlexDateRangeField::make('period')->rangePresets([]);
```

This removes the complete shortcut rail, including its mobile row. Calendar selection and Apply/Cancel remain available.

## Your own periods

```php theme={"theme":{"light":"github-light","dark":"github-dark"}}
FlexDateRangeField::make('campaign')
    ->rangePresets([
        ['label' => 'Launch week', 'start' => '2026-10-12', 'end' => '2026-10-18'],
        ['label' => 'Autumn campaign', 'start' => '2026-10-01', 'end' => '2026-11-30'],
    ]);
```

The list replaces the built-in shortcuts. Each item needs a nonempty label and valid ordered `Y-m-d` endpoints. Up to 30 custom presets are accepted. Presets do not bypass bounds or availability; Apply stays disabled for an invalid selection.

## Dynamic periods

```php theme={"theme":{"light":"github-light","dark":"github-dark"}}
use Carbon\CarbonImmutable;

FlexDateRangeField::make('report')
    ->rangePresets(function (): array {
        $today = CarbonImmutable::today('Europe/Warsaw');

        return [
            ['label' => 'Today', 'start' => $today->toDateString(), 'end' => $today->toDateString()],
            ['label' => 'Next 14 days', 'start' => $today->toDateString(), 'end' => $today->addDays(13)->toDateString()],
        ];
    });
```

A PHP closure is evaluated when the field is rendered. To restore built-ins, use `rangePresets(null)`. If a page stays open across midnight, refresh the field/page to refresh its reporting shortcuts.

## One or two months

```php Responsive default theme={"theme":{"light":"github-light","dark":"github-dark"}}
FlexDateRangeField::make('period')->calendarMonths('auto');
```

```php One month theme={"theme":{"light":"github-light","dark":"github-dark"}}
FlexDateRangeField::make('period')->calendarMonths(1);
```

```php Two months where space allows theme={"theme":{"light":"github-light","dark":"github-dark"}}
FlexDateRangeField::make('period')->calendarMonths(2);
```

Small viewports always use the compact one-month layout, even if two months are requested. The preset rail becomes a scrolling row. On short screens the calendar body scrolls while confirmation controls remain reachable. Input values stay within the field width; the panel shows a complete summary.

## Include start and end time

```php theme={"theme":{"light":"github-light","dark":"github-dark"}}
FlexDateRangeField::make('booking')
    ->granularity('minute')
    ->hourCycle(12)
    ->minuteStep(15)
    ->timeZone('Europe/Warsaw')
    ->default([
        'start' => '2026-10-10T08:45:00',
        'end' => '2026-10-15T18:45:00',
    ]);
```

Clock controls share a row on desktop. On mobile their labels sit above each control, and the footer shows start and end on separate lines. All stored endpoints use the same `storageFormat()`.

```php theme={"theme":{"light":"github-light","dark":"github-dark"}}
FlexDateRangeField::make('booking')->granularity('minute')->hourCycle(24)->hideTimeZone();
FlexDateRangeField::make('booking')->granularity('second')->showSeconds()->secondStep(5);
FlexDateRangeField::make('booking')->granularity('hour')->defaultTime('09:00');
```

Date-only is the default. `showSeconds()` promotes an already timed range to second precision; it does not turn a day-only range into a timed one.

## Available appointment hours

```php theme={"theme":{"light":"github-light","dark":"github-dark"}}
FlexDateRangeField::make('booking')
    ->granularity('minute')
    ->allowedTimes(['09:00', '12:00', '17:00'])
    ->defaultTime('09:00');
```

Both endpoints must use one of those times. `minValue()`/`maxValue()` can additionally bound the complete date/time period. Use `disabledDates()` and `disabledWeekdays()` to block endpoint days.

## Range validation

```php theme={"theme":{"light":"github-light","dark":"github-dark"}}
FlexDateRangeField::make('leave')
    ->required()
    ->minDate('2026-10-01')
    ->maxDate('2026-12-31')
    ->allowSameDay(false)
    ->disabledDates(['2026-12-25']);
```

Both endpoints must exist, be valid and meet the configured constraints. End cannot precede start. `allowSameDay(false)` requires different calendar days, including for timed ranges. Bounds are inclusive. Availability checks apply to endpoints; an unavailable day inside the period does not automatically invalidate it. Add a custom rule when every intervening day must be available.

## Minimum number of days

```php theme={"theme":{"light":"github-light","dark":"github-dark"}}
use Carbon\CarbonImmutable;

FlexDateRangeField::make('stay')
    ->required()
    ->rules([
        function (string $attribute, mixed $value, Closure $fail): void {
            if (!is_array($value) || !isset($value['start'], $value['end'])) {
                return;
            }

            try {
                $days = CarbonImmutable::parse($value['start'])->diffInDays(CarbonImmutable::parse($value['end']), false);
            } catch (Throwable) {
                return;
            }

            if ($days < 2) {
                $fail('Choose a stay of at least two nights.');
            }
        },
    ]);
```

Built-in date validation still applies alongside your rule. Use the configured storage format when parsing custom formats in your own rules.

## Editable endpoints

```php theme={"theme":{"light":"github-light","dark":"github-dark"}}
FlexDateRangeField::make('period')
    ->editable()
    ->displayFormat('d/m/Y')
    ->rangeSeparator(' – ');
```

Numeric segments follow your display format, including date/time segments when enabled. Arbitrary free text is not accepted as a date. An incomplete endpoint cannot submit successfully. On narrow inputs, scroll the segments or navigate them with the keyboard.

## Clearing and inline selection

```php theme={"theme":{"light":"github-light","dark":"github-dark"}}
FlexDateRangeField::make('period')->clearable();
FlexDateRangeField::make('period')->inline()->rangePresets([]);
FlexDateRangeField::make('period')->fixedWeeks()->weekStartsOn(1);
```

Ranges are not clearable by default. Inline ranges also require Apply to confirm a draft; Cancel restores the previous value. The range grid displays days belonging to each shown month; `outsideDays()` applies to single-date calendars rather than adding duplicate days to a range grid.

## Appearance

```php theme={"theme":{"light":"github-light","dark":"github-dark"}}
FlexDateRangeField::make('period')
    ->size('sm')
    ->variant('secondary')
    ->locale('pl')
    ->displayFormat('d.m.Y')
    ->weekStartsOn(1);
```

The trigger uses the shared input appearance. The range panel expands from that trigger and returns to it on closing, including when the input label is clipped. Reduced-motion preferences remove the spring morph. See [events](https://flex-fields.bjanczak.com/docs/date-time-events-and-validation.md) to respond to confirmed changes.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.