Date picker component that composes Calendar and a popover-style dropdown.
The date picker is fully server-rendered. The parent LiveView owns all state
(open, value, current_month) and handles the events emitted by the
trigger button and the embedded calendar. There is no client-side JS hook.
Sub-components
date_picker/1— standalone date picker with trigger button + calendar dropdownform_date_picker/1—Phoenix.HTML.FormField-integrated variant with hidden input and changeset error display
When to use
Use date_picker/1 for any date-selection field in a form or settings panel —
booking dates, birth dates, expiry dates, due dates, etc.
Use form_date_picker/1 when the field is part of a Phoenix form backed by an
Ecto changeset, so validation errors are automatically displayed.
For selecting a date range (check-in / check-out, report period), use
date_range_picker/1 instead.
Standalone example
defmodule MyAppWeb.EventLive do
use Phoenix.LiveView
def mount(_params, _session, socket) do
{:ok, assign(socket,
picker_open: false,
selected_date: nil,
current_month: Date.beginning_of_month(Date.utc_today())
)}
end
def handle_event("toggle-picker", _params, socket) do
{:noreply, update(socket, :picker_open, &(!&1))}
end
def handle_event("date-selected", %{"date" => iso}, socket) do
date = Date.from_iso8601!(iso)
{:noreply, assign(socket, selected_date: date, picker_open: false)}
end
def handle_event("calendar-prev-month", %{"month" => iso}, socket) do
{:noreply, assign(socket, current_month: Date.from_iso8601!(iso))}
end
def handle_event("calendar-next-month", %{"month" => iso}, socket) do
{:noreply, assign(socket, current_month: Date.from_iso8601!(iso))}
end
end
<%!-- Template --%>
<.date_picker
id="event-date"
open={@picker_open}
value={@selected_date}
current_month={@current_month}
on_toggle="toggle-picker"
on_change="date-selected"
min={Date.utc_today()}
placeholder="Select event date"
/>Form-integrated example
<.form for={@form} phx-submit="save_event">
<.form_date_picker
id="start-date-picker"
field={@form[:start_date]}
open={@picker_open}
value={@selected_date}
current_month={@current_month}
on_toggle="toggle-picker"
on_change="date-selected"
placeholder="Pick a start date"
/>
<.button type="submit">Save</.button>
</.form>Date format
The :format attribute uses Calendar.strftime/2 directives:
| Format string | Output example |
|---|---|
"%d/%m/%Y" | "25/12/2026" |
"%B %d, %Y" | "December 25, 2026" |
"%Y-%m-%d" | "2026-12-25" |
Summary
Functions
Renders a date picker with a trigger button and a calendar dropdown.
Renders a date picker integrated with Phoenix.HTML.FormField.
Functions
Renders a date picker with a trigger button and a calendar dropdown.
The parent LiveView must handle four events:
on_toggle— toggle the popover open/closedon_change— a day was clicked (close the picker and updatevalue)"calendar-prev-month"— navigate to the previous month"calendar-next-month"— navigate to the next month
The calendar dropdown is conditionally rendered with :if={@open}, so it
is completely absent from the DOM when closed (no CSS visibility tricks).
Attributes
id(:string) (required) - Unique date picker ID — also used as the calendar'sidprefix.value(:any) - Selected date (Date.t()) ornilwhen no date has been chosen. Defaults tonil.current_month(:any) - Currently displayed month (Date.t()). Whennil, defaults to the month of:value, or the current month if:valueis also nil. Update this in response tocalendar-prev-month/calendar-next-monthevents.Defaults to
nil.open(:boolean) - Whether the calendar dropdown is currently visible. Controlled by the LiveView. Defaults tofalse.placeholder(:string) - Trigger button text displayed when no date is selected. Defaults to"Pick a date".format(:string) -Calendar.strftime/2format string used to display the selected date in the trigger. Defaults to"%d/%m/%Y"(e.g."25/12/2026").Defaults to
"%d/%m/%Y".on_toggle(:string) -phx-clickevent name for the trigger button to toggle the dropdown open/closed. Defaults to"date-picker-toggle".on_change(:string) -phx-clickevent fired when a day is clicked. Receives%{"date" => "YYYY-MM-DD"}. Defaults to"calendar-change".min(:any) - Minimum selectable date (Date.t()). Passed through to the embeddedcalendar/1. Defaults tonil.max(:any) - Maximum selectable date (Date.t()). Passed through to the embeddedcalendar/1. Defaults tonil.disabled_dates(:list) - List ofDate.t()values that cannot be selected. Passed through tocalendar/1. Defaults to[].class(:string) - Additional CSS classes merged onto the root element. Defaults tonil.Global attributes are accepted. HTML attributes forwarded to the root div element.
Renders a date picker integrated with Phoenix.HTML.FormField.
Renders a <input type="hidden"> bound to field.name with the ISO 8601
date value. This hidden input is what gets submitted with the form so Phoenix
changesets receive a string they can cast with cast/3.
Changeset validation errors from field.errors are rendered as destructive
text below the picker.
Changeset integration
Cast the hidden input value in your changeset:
def changeset(event, attrs) do
event
|> cast(attrs, [:start_date])
|> validate_required([:start_date])
endThe hidden input sends an ISO 8601 string ("2026-03-15"), which Ecto's
Date type will automatically parse during cast/3.
Attributes
field(Phoenix.HTML.FormField) (required) -Phoenix.HTML.FormFieldstruct from@form[:field_name]. Provides theid,name, anderrorsfor form integration.id(:string) (required) - Unique date picker ID (separate fromfield.idwhich is used for the hidden input).value(:any) - Selected date (Date.t()) ornil. Defaults tonil.current_month(:any) - Currently displayed month (Date.t()). Defaults tonil.open(:boolean) - Whether the popover is open. Defaults tofalse.placeholder(:string) - Placeholder text. Defaults to"Pick a date".format(:string) -Calendar.strftime/2format string for displaying the selected date. Defaults to"%d/%m/%Y".on_toggle(:string) -phx-clickevent name for the trigger button. Defaults to"date-picker-toggle".on_change(:string) -phx-clickevent name for day clicks in the embedded calendar. Defaults to"calendar-change".class(:string) - Additional CSS classes for the wrapper div. Defaults tonil.