- Accordion
- Alert
- Alert Dialog
- Aspect Ratio
- Attachment
- Avatar
- Badge
- Banner
- Breadcrumb
- Bubble
- Button
- Button Group
- Calendar
- Card
- Carousel
- Chart
- Checkbox
- Command
- Collapsible
- Combobox
- Context Menu
- Data Table
- Date Picker
- Dialog
- Dropdown Menu
- Editor
- Input
- Input Group
- Label
- Loading Spinner
- Native Select
- Popover
- Select
- Separator
- Sheet
- Sidebar
- Skeleton
- Slider
- Steps
- Switch
- Tabs
- Textarea
- Tooltip
Calendar
A date field component that allows users to enter and edit date.
<april:calendar class="rounded-md border" mode="single" :selected="now()" />
<april:calendar class="rounded-md border" mode="single" :selected="now()" />
This calendar was inspired by the React DayPicker and follows the same composable conventions as shadcn/ui's calendar.
The calendar supports single, multiple, and range selection, keyboard navigation, month/year controls, and disabled-date matchers.
| Prop | Type | Description |
|---|---|---|
mode |
"single" |
"multiple" |
"range" |
Selection mode. Defaults to
single. |
selected |
string |
Date | mode-specific array |
Initial selection. |
disabled |
array |
Disabled-date matchers. |
required |
boolean |
Prevent clearing the current selection. |
Common configurations
The default calendar keeps the original April UI appearance. These options add common shadcn calendar variants without requiring a different component.
| Prop | Type | Description |
|---|---|---|
captionLayout |
"label" |
"dropdown" |
"dropdown-months" |
"dropdown-years" |
Text labels or native month/year selectors. |
showOutsideDays |
boolean |
Render days from adjacent months. Defaults to
true. |
fixedWeeks |
boolean |
Keep six rows for a stable height. Defaults to
true. |
showWeekNumber |
boolean |
Add ISO week numbers to the grid. |
numberOfMonths |
int |
Render up to twelve consecutive months. |
pagedNavigation |
boolean |
Move by the number of visible months. |
weekStartsOn |
0–
6 |
First weekday (
0 is Sunday). |
defaultMonth |
string |
Date |
Month shown when no selection exists. |
fromMonth /
startMonth |
string |
Date |
Earliest navigable month. |
toMonth /
endMonth |
string |
Date |
Latest navigable month. |
fromYear /
toYear |
int |
Bounds for the year dropdown. |
hideNavigation |
boolean |
Hide previous/next controls. |
The component dispatches
value-change with
{ detail: { value } }. The existing
change and
select events remain available:
<april:calendar captionLayout="dropdown" :showWeekNumber="true" :showOutsideDays="false" :fromYear="now()->subYears(2)->year" :toYear="now()->addYears(2)->year" @value-change="console.log($event.detail.value)"/>
Dropdown caption and week numbers
<april:calendar class="rounded-md border" captionLayout="dropdown" :showOutsideDays="false" :showWeekNumber="true" :fromYear="now()->subYears(2)->year" :toYear="now()->addYears(2)->year" :defaultMonth="now()->startOfMonth()"/>
<april:calendar class="rounded-md border" captionLayout="dropdown" :showOutsideDays="false" :showWeekNumber="true" :fromYear="now()->subYears(2)->year" :toYear="now()->addYears(2)->year" :defaultMonth="now()->startOfMonth()"/>
Multiple months
<april:calendar class="rounded-md border" :numberOfMonths="2" :pagedNavigation="true" :showWeekNumber="true" :showOutsideDays="false" :defaultMonth="now()->startOfMonth()"/>
<april:calendar class="rounded-md border" :numberOfMonths="2" :pagedNavigation="true" :showWeekNumber="true" :showOutsideDays="false" :defaultMonth="now()->startOfMonth()"/>
Selection modes
Single mode
When
mode="single", one day can be selected at a time. The
select event's
$event.detail.value contains a JavaScript
Date or
null.
<div x-data="{value: null}" class="max-w-72"> <april:calendar class="rounded-md border" mode="single" :selected="now()" @value-change="value = $event.detail.value" /> <p x-text="value ? 'Selected date is '+value : 'No Date Is Selected' " class="my-2 w-full"></p></div>
<div x-data="{value: null}" class="max-w-72"> <april:calendar class="rounded-md border" mode="single" :selected="now()" @value-change="value = $event.detail.value" /> <p x-text="value ? 'Selected date is '+value : 'No Date Is Selected' " class="my-2 w-full"></p></div>
| Prop | Type | Description |
|---|---|---|
@select |
Alpine event listener |
Runs when the selected day changes. |
required |
boolean |
Ensures a selected day cannot be cleared. |
Multiple mode
When
mode="multiple", users can select several days. The event value is an array of JavaScript
Date objects.
<div x-data="{ value: [] }" @value-change="value = $event.detail.value" class="flex max-w-72 flex-col items-center"> <april:calendar class="rounded-md border" mode="multiple" :selected="[now(), now()->addDays(2), now()->addDays(14)]" /> <p x-text="'Selected dates are '+value.toString()" class="my-2 wrap-break-word"></p></div>
<div x-data="{ value: [] }" @value-change="value = $event.detail.value" class="flex max-w-72 flex-col items-center"> <april:calendar class="rounded-md border" mode="multiple" :selected="[now(), now()->addDays(2), now()->addDays(14)]" /> <p x-text="'Selected dates are '+value.toString()" class="my-2 wrap-break-word"></p></div>
Use
max to limit the number of selected days.
<april:calendar mode="multiple" max="5" />
<div x-data="{ value: [] }" @value-change="value = $event.detail.value" class="flex max-w-72 flex-col items-center"> <april:calendar class="rounded-md border" mode="multiple" max="5" :selected="[now(), now()->addDays(2), now()->addDays(14)]" /> <p x-text="'Selected dates are '+value.toString()" class="my-2 wrap-break-word"></p></div>
<div x-data="{ value: [] }" @value-change="value = $event.detail.value" class="flex max-w-72 flex-col items-center"> <april:calendar class="rounded-md border" mode="multiple" max="5" :selected="[now(), now()->addDays(2), now()->addDays(14)]" /> <p x-text="'Selected dates are '+value.toString()" class="my-2 wrap-break-word"></p></div>
Range mode
When
mode="range", the event value is an object with
from and
to JavaScript
Date values.
min and
max limit the number of days between the endpoints.
<div x-data="{ value: { from: null, to: null } }" @value-change="value = $event.detail.value" class="flex w-full max-w-72 flex-col items-center"> <april:calendar class="rounded-md border" mode="range" :selected="['from' => now(), 'to' => now()->addDays(14)]" /> <p x-text="'Selected range is from '+value['from']+' to '+value['to']" class="my-2 wrap-break-word"></p></div>
<div x-data="{ value: { from: null, to: null } }" @value-change="value = $event.detail.value" class="flex w-full max-w-72 flex-col items-center"> <april:calendar class="rounded-md border" mode="range" :selected="['from' => now(), 'to' => now()->addDays(14)]" /> <p x-text="'Selected range is from '+value['from']+' to '+value['to']" class="my-2 wrap-break-word"></p></div>
Use
min and
max to constrain the length of the selected range:
<april:calendar mode="range" min="3" max="14" />
<div x-data="{ value: { from: null, to: null } }" @value-change="value = $event.detail.value" class="flex w-full max-w-72 flex-col items-center"> <april:calendar class="rounded-md border" mode="range" :selected="['from' => now(), 'to' => now()->addDays(14)]" max="15" min="6" /> <p x-text="'Selected range is from '+value['from']+' to '+value['to']" class="my-2 wrap-break-word"></p></div>
<div x-data="{ value: { from: null, to: null } }" @value-change="value = $event.detail.value" class="flex w-full max-w-72 flex-col items-center"> <april:calendar class="rounded-md border" mode="range" :selected="['from' => now(), 'to' => now()->addDays(14)]" max="15" min="6" /> <p x-text="'Selected range is from '+value['from']+' to '+value['to']" class="my-2 wrap-break-word"></p></div>
Disabling dates
Pass an array of matcher objects to
disabled. A
dates matcher disables exact dates,
before/
after disables a range (the boundary itself remains enabled), and
dayOfWeek accepts a number or an array from
0 (Sunday) to
6 (Saturday).
Laravel serializes
now() on the server. April converts ISO timestamps to the visitor's local calendar date in the browser, so a date-only rule such as
now() follows the user's local day. If your application stores a timezone per user, apply it before building the value, for example
now()->setTimezone($user->timezone).
<april:calendar class="rounded-md border" mode="single" :disabled="['dates' => [now()]]" />
<april:calendar class="rounded-md border" mode="single" :disabled="['dates' => [now()]]" />
<april:calendar class="rounded-md border" mode="single" :disabled="['before' => now(), 'after' => now()->addDays(10)]" />
<april:calendar class="rounded-md border" mode="single" :disabled="['before' => now(), 'after' => now()->addDays(10)]" />
Disable the worst day of the week
<div class="flex flex-col items-center gap-2 text-center"> <p>Disable the worst day of the week</p> <april:calendar class="rounded-md border" mode="single" :disabled="['dayOfWeek' => 1]" /></div>
<div class="flex flex-col items-center gap-2 text-center"> <p>Disable the worst day of the week</p> <april:calendar class="rounded-md border" mode="single" :disabled="['dayOfWeek' => 1]" /></div>
<april:calendar class="rounded-md border" mode="single" :disabled="[ ['before' => now()->subDays(10), 'after' => now()->addDays(10)], ['dates' => [now(), now()->subDays(1)]], ['dayOfWeek' => 3], ['dayOfWeek' => 5] ]" />
<april:calendar class="rounded-md border" mode="single" :disabled="[ ['before' => now()->subDays(10), 'after' => now()->addDays(10)], ['dates' => [now(), now()->subDays(1)]], ['dayOfWeek' => 3], ['dayOfWeek' => 5] ]" />
The same matcher types can be used independently:
<april:calendar :disabled="[ ['dates' => [now(), now()->addDay()]], ['before' => now()], ['dayOfWeek' => [0, 6]],]" />
Publishing Views
Use the following command to publish this view:
php artisan april:publish calendar
php artisan april:publish calendar
php artisan april:publish --all
php artisan april:publish --all