Date Field
An enhanced alternative to a native date input.
Features
- Full keyboard navigation
- Localization support
- Can be controlled or uncontrolled
- Focus is fully managed
- Accessible by default
- Supports both date and date-time formats
Overview
The Date Field is an enhanced alternative to the rather limited native date input. It provides a more user-friendly interface for selecting dates and times, is fully accessible, and works in all modern browsers & devices.
Anatomy
- Field: The element which contains the date segments
- Segment: An individual segment of the date (day, month, year, etc.)
- Label: The label for the date field
- Hidden Input: A hidden input element containing the value of the field
- Validation: The container for the
Tutorial
Learn how to use the Date Field builder by starting simple and working your way up to more complex examples and use cases. The goal is to teach you the key features and concepts of the builder, so you won’t have to read through a bunch of API reference docs (Although, those are available too!)
Building a Date Field
Let's initialize a new Date Field using the createDateField
function, which returns an object
consisting of the stores & methods needed to construct the field.
To start off, we'll destructure the field
, segment
, and label
, and segmentContents
.
The field
is responsible for containing the date segments. The label
for the date field is not
an actual <label>
element, due to the way we interact with the field, but is still accessible to
screen readers in the same way.
Unlike the other elements, segment
is a function which takes a SegmentPart
as an argument,
which is used to determine which segment this element represents.
While it's possible to use the segment
function to render each segment individually like so:
It's not recommended, as the formatting doesn't adapt to the locale and type of date being represented, which is one of the more powerful features this builder provides.
Instead, you can use the segmentContents
state, which is an array of objects necessary to form the
date. Each object has a part
, which is the SegmentPart
, and a value
,
which is the locale-aware string representation of the segment.
To actually use the value of the field, you can either use the value
state directly, or if you're
using it within a form, the hiddenInput
element, which is a hidden input element containing an ISO
8601 formatted string of the value
.
If you plan on using the hiddenInput
, you'll need to pass the name
prop, which will be used as
its name.
And that, along with some additional structure and styles, is all you need to get a fully functional Date Field!
The Power of Placeholder
In the previous example, we didn't pass any props to the createDateField
function, which means it
defaulted to a CalendarDate
object with the current date. What if we wanted to start off with a
different type of date, such as a CalendarDateTime
or a ZonedDateTime
, but keep the initial
value empty?
That's where the placeholder props come in, which consist of the uncontrolled defaultPlaceholder
prop, and the controlled placeholder
prop.
In the absense of a value
or defaultValue
prop (which we'll cover soon), the placeholder holds
all the power in determining what type of date the field represents, and how it should be
rendered/formatted.
Let's convert our previous example into a Date & Time field, by passing a CalendarDateTime
object
as the defaultPlaceholder
prop.
As you can see above, by making that one change, the field now represents a date and time, and the segments have been updated to reflect that.
If your placeholder value is coming from a database or elsewhere, you can use one of the parser
functions provided by
@internationalized/date to
convert it into a CalendarDateTime
object.
We can also just as easily convert the field into a Zoned Date & Time field, by passing a
ZonedDateTime
object as the defaultPlaceholder
prop.
We're using the now
parser function to create a ZonedDateTime
object with the current date and
time, and we're getting the user's local timezone using the getLocalTimeZone
function.
Alternatively, we can hardcode the timezone to something like America/Los_Angeles
by passing it as
the argument to the now
function.
How you represent and store dates with timezones will depend entirely on your use case, but there are a number of parser functions available to help you out, which you can read more about here .
Working with Values
Now that we've covered the basics of the Date Field, as well as the power of the placeholder, let's
look at how we can use the defaultValue
to set the value of the field.
Remember, the placeholder props are only relevant in the absense of a value
or defaultValue
prop, as they take precedence over the placeholder. When a value
or defaultValue
prop is passed,
the placeholder is set to that value, and the field will represent that type of date.
We can demonstrate this by keeping the defaultPlaceholder
prop as a CalendarDateTime
object, and
passing a CalendarDate
(without time) object as the defaultValue
prop. It's not recommended that
you ever set them to different types, but it's useful to understand how the placeholder & value
props interact.
As you can see, the field represents a CalendarDate
object, and even if you clear the field, it
will retain that shape.
A really useful scenario for using the defaultValue
prop and the defaultPlaceholder
prop in
conjunction with one another is when a defaultValue
may or may not be present, and you want to
ensure the field always represents a certain type of date.
For example, let's say this field is for a user's birthday, which is optional, but you want to
ensure that if they do enter a birthday, it's represented as a CalendarDate
object. The code to
accomplishing that may look something like this:
If the user has a birthday, we parse the ISO 8601 formatted string into a CalendarDate
object, and
if not, we pass undefined
as the defaultValue
prop, which will cause the field to default to the
defaultPlaceholder
prop, which we've set to a CalendarDate
object.
The following example demonstrates how it would work in both scenarios (with and without a birthday).
Validating Dates
This is where things start to get a lot more fun! The Date Field builder provides a few ways to
validate dates, which we'll cover in this section, starting with the isDateUnavailable
prop.
The isDateUnavailable
prop is a Matcher
function, which takes a DateValue
object as an
argument, and returns a boolean indicating whether or not that date is unavailable.
If the date the user selects is unavailable, is marked as invalid, and you can do whatever you'd like with that information.
Let's say that we don't want users to ever be able to select the 1st or the 15th of any month, as
those are the only two days we're not working on builder tutorials. We can setup a Matcher
function to accomplish just that.
If you have a few different matchers you want to use, you can simply combine them like so:
Or if you want to get really fancy with it, you can create a helper function that takes an array of matchers which you could use throughout your app.
When a field is marked as invalid, the isInvalid
store will be set to true
, and a data-invalid
attribute will be added to all the elements that make up the field, which you can use to style the
field however you'd like.
You'll want to use the validation
element to display a message to the user indicating why the date
is invalid. It's automatically hidden when the field is valid, and is wired up via aria attributes
to give screen readers the information they need.
Here's an example to get an idea of what you might do. Attempt to enter an unavailable date, and you'll see the behavior in action.
The Date Field builder also accepts minValue
and maxValue
props to set the minimum and maximum
dates a user can select.
In this example, we're limiting the selection dates to between October 11th, 2023 and October 11th, 2024.
Locale-aware Formatting
One of the coolest features of the Date Field builder is the ability to automatically format the segments and placeholder based on the locale.
Of course it's up to you to decide how you get your user's locale, but once you have it, it's as
simple as passing it as the locale
prop.
Here's an example showcasing a few different locales:
Notice that they all have the same defaultPlaceholder
, yet the segments are formatted differently
depending on the locale, and all it took was changing the locale
prop. Pretty cool, right?
Readonly Segments
The entire Date Field builder can be set as readonly using the readonly
prop. But in some
situations, you may want a user to be able to edit the time but not the date, or the month but not
the year.
The readonlySegments
prop can be used for this.
In this example, the year
segment is readonly, but the month
and day
segments are focusable
and editable.
API Reference
createDateField
The builder function used to create the date field component.
Props
Prop | Default | Type / Description |
defaultValue | - | DateValue | undefined The default value for the date field. When provided the |
value | - | Writable<DateValue | undefined> A writable store than can be used to control the value of the date field from outside the builder. Useful if you want to sync the value of the date field with another store used in your app. |
onValueChange | - | ChangeFn<DateValue | undefined> A function called when the value of the date field changes. It receives a single argument, which is an object containing |
defaultPlaceholder | CalendarDate | DateValue The date that is used when the date field is empty to determine what point in time the field should start at. |
placeholder | - | Writable<DateValue> A writable store that can be used to control the placeholder date from outside the builder. When this prop is provided, the |
onPlaceholderChange | - | ChangeFn<DateValue> A function called when the placeholder value changes. It receives a single argument, which is an object containing |
isDateUnavailable | - | Matcher | undefined A function that accepts a date and returns a boolean indicating whether the date is unavailable. |
minValue | - | DateValue | undefined The minimum date that can be selected. |
maxValue | - | DateValue | undefined The maximum date that can be selected. |
disabled | false | boolean Whether the date field is disabled. |
readonly | false | boolean Whether the date field is readonly. |
readonlySegments | - | EditableSegmentPart[] The set of segments that are readonly. |
hourCycle | - | HourCycle The hour cycle to use when formatting the date. |
locale | 'en' | string The locale to use when formatting the date. |
granularity | - | 'day' | 'hour' | 'minute' | 'second' The granularity of the date field. Defaults to |
name | - | string The name of the hidden input element. |
required | false | boolean Whether the hidden input is required. |
ids | - | DateFieldIds Override the default ids used by the various elements within the date field. |
Elements
Element | Description |
The element which contains the date segments | |
An individual segment of the date | |
The label for the date field | |
The element containing the validation message | |
The hidden input used to submit the value within a form |
States
State | Description |
value | A writable store which represents the current value of the date field. |
segmentValues | A writable store containing the current values of the date segments. |
segmentContents | A readable store used to dynamically render the date segments. |
segmentContentsObj | A readable store containing the current values of the date segments. |
placeholder | A writable store which represents the placeholder value of the date field. |
isInvalid | A readable store which represents whether the date field is invalid. |
isDateUnavailable | A readable store which returns a function that accepts a date and returns a boolean indicating whether the date is unavailable. |
Options
Option | Description |
defaultValue | The default value for the date field. When provided the |
onValueChange | A function called when the value of the date field changes. It receives a single argument, which is an object containing |
defaultPlaceholder | The date that is used when the date field is empty to determine what point in time the field should start at. |
onPlaceholderChange | A function called when the placeholder value changes. It receives a single argument, which is an object containing |
isDateUnavailable | A function that accepts a date and returns a boolean indicating whether the date is unavailable. |
minValue | The minimum date that can be selected. |
maxValue | The maximum date that can be selected. |
disabled | Whether the date field is disabled. |
readonly | Whether the date field is readonly. |
readonlySegments | The set of segments that are readonly. |
hourCycle | The hour cycle to use when formatting the date. |
locale | The locale to use when formatting the date. |
granularity | The granularity of the date field. Defaults to |
name | The name of the hidden input element. |
required | Whether the hidden input is required. |
ids | Override the default ids used by the various elements within the date field. |
field
The element which contains the date segments
Data Attributes
Data Attribute | Value |
[data-invalid] | Present when the date field is invalid. |
[data-disabled] | Present when the date field is disabled. |
[data-melt-datefield-field] | Present on all field elements. |
segment
An individual segment of the date
Data Attributes
Data Attribute | Value |
[data-invalid] | Present when the field is invalid. |
[data-disabled] | Present when the field is disabled. |
[data-segment] | SegmentPart |
[data-melt-datefield-segment] | Present on all segment elements. |
Custom Events
Event | Value |
m-keydown | (e: ) => void |
m-focusout | (e: ) => void |
m-click | (e: ) => void |
label
The label for the date field
Data Attributes
Data Attribute | Value |
[data-invalid] | Present when the field is invalid. |
[data-melt-datefield-label] | Present on all label elements. |
validation
The element containing the validation message
Data Attributes
Data Attribute | Value |
[data-invalid] | Present when the field is invalid. |
[data-melt-datefield-validation] | Present on all validation elements. |
hiddenInput
The hidden input for the date field
Data Attributes
Data Attribute | Value |
[data-melt-datefield-hiddenInput] | Present on all hiddenInput elements. |
Custom Events
Event | Value |
m-click | (e: ) => void |
m-keydown | (e: ) => void |
On This Page