Date Range Field

Allows users to input a range of dates within a designated field.

Hotel dates
	<script lang="ts">
  import { DateRangeField } from "bits-ui";
</script>
 
<DateRangeField.Root class="flex w-full max-w-[320px] flex-col gap-1.5">
  <DateRangeField.Label class="block select-none text-sm font-medium">
    Hotel dates
  </DateRangeField.Label>
  <div
    class="flex h-input w-full select-none items-center rounded-input border border-border-input bg-background px-2 py-3 text-sm tracking-[0.01em] text-foreground focus-within:border-border-input-hover focus-within:shadow-date-field-focus hover:border-border-input-hover"
  >
    {#each ["start", "end"] as const as type}
      <DateRangeField.Input {type}>
        {#snippet children({ segments })}
          {#each segments as { part, value }}
            <div class="inline-block select-none">
              {#if part === "literal"}
                <DateRangeField.Segment
                  {part}
                  class="p-1 text-muted-foreground"
                >
                  {value}
                </DateRangeField.Segment>
              {:else}
                <DateRangeField.Segment
                  {part}
                  class="rounded-5px px-1 py-1 hover:bg-muted focus:bg-muted focus:text-foreground focus-visible:!ring-0 focus-visible:!ring-offset-0 aria-[valuetext=Empty]:text-muted-foreground"
                >
                  {value}
                </DateRangeField.Segment>
              {/if}
            </div>
          {/each}
        {/snippet}
      </DateRangeField.Input>
      {#if type === "start"}
        <div aria-hidden="true" class="px-1 text-muted-foreground">–⁠⁠⁠⁠⁠</div>
      {/if}
    {/each}
  </div>
</DateRangeField.Root>

Structure

	<script lang="ts">
	import { DateField } from "$lib";
</script>
 
<DateRangeField.Root>
	<DateRangeField.Label>Check-in date</DateRangeField.Label>
	{#each ["start", "end"] as const as type}
		<DateRangeField.Input {type}>
			{#snippet children({ segments })}
				{#each segments as { part, value }}
					<DateRangeField.Segment {part}>
						{value}
					</DateRangeField.Segment>
				{/each}
			{/snippet}
		</DateRangeField.Input>
	{/each}
</DateRangeField.Root>

Placeholder State

Bits UI provides flexible options for controlling and synchronizing the DateRangeField component's placeholder state.

Two-Way Binding

Use the bind:placeholder directive for effortless two-way synchronization between your local state and the DateRangeField component's placeholder.

	<script lang="ts">
	import { DateRangeField } from "bits-ui";
	let placeholder = $state(new CalendarDateTime(2024, 8, 3, 12, 30));
</script>
 
<DateRangeField.Root bind:placeholder>
	<!-- ... -->
</DateField.Root>

This setup enables toggling the DateRangeField component's placeholder via the custom button and ensures the local placeholder state is synchronized with the DateRangeField component's placeholder should it change from within the component.

Change Handler

You can also use the onPlaceholderChange prop to update local state when the component's placeholder changes. This is useful when you don't want two-way binding for one reason or another, or you want to perform additional logic when the DateRangeField component's placeholder changes.

	<script lang="ts">
	import { DateRangeField } from "bits-ui";
	let placeholder = $state(new CalendarDateTime(2024, 8, 3, 12, 30));
</script>
 
<DateRangeField.Root
	bind:placeholder
	onPlaceholderChange={(p) => {
		placeholder = placeholder.set({ year: 2025 });
	}}
>
	<!-- ... -->
</DateRangeField.Root>

Controlled

Sometimes, you may want complete control over the placeholder state, meaning you will be "kept in the loop" and be required to apply the state change yourself. While you will rarely need this, it's possible to do so by setting the controlledPlaceholder prop to true.

You will then be responsible for updating a local placeholder state variable that is passed as the placeholder prop to the DateRangeField.Root component.

	<script lang="ts">
	import { DateRangeField } from "bits-ui";
 
	let myPlaceholder = $state();
</script>
 
<DateRangeField.Root
	controlledPlaceholder
	placeholder={myPlaceholder}
	onPlaceholderChange={(p) => (myPlaceholder = p)}
>
	<!-- ... -->
</DateRangeField.Root>

See the Controlled State documentation for more information about controlled states.

Value State

The value represents the currently selected date within the DateRangeField component.

Bits UI provides flexible options for controlling and synchronizing the DateRangeField component's value state.

Two-Way Binding

Use the bind:value directive for effortless two-way synchronization between your local state and the DateRangeField component's value.

	<script lang="ts">
	import { DateRangeField } from "bits-ui";
	let value = $state({
		start: new CalendarDateTime(2024, 8, 3, 12, 30),
		end: new CalendarDateTime(2024, 8, 4, 12, 30),
	});
</script>
 
<button
	onclick={() => {
		value = {
			start: value.start.add({ days: 1 }),
			end: value.end.add({ days: 1 }),
		};
	}}
>
	Add 1 day
</button>
<DateRangeField.Root bind:value>
	<!-- ... -->
</DateRangeField.Root>

This setup enables toggling the component's value via the custom button and ensures the local value state is synchronized with the component's value, should it change from within the component.

Change Handler

You can also use the onValueChange prop to update local state when the component's value changes. This is useful when you don't want two-way binding for one reason or another, or you want to perform additional logic when the component's value changes.

	<script lang="ts">
	import { DateRangeField } from "bits-ui";
	let value = $state({
		start: new CalendarDateTime(2024, 8, 3, 12, 30),
		end: new CalendarDateTime(2024, 8, 4, 12, 30),
	});
</script>
 
<DateRangeField.Root
	bind:value
	onValueChange={(v) => {
		value = {
			start: v.start.set({ hour: v.start.hour + 1 }),
			end: v.end.set({ hour: v.end.hour + 1 }),
		};
	}}
>
	<!-- ... -->
</DateRangeField.Root>

Controlled

Sometimes, you may want complete control over the component's value state, meaning you will be "kept in the loop" and be required to apply the state change yourself. While you will rarely need this, it's possible to do so by setting the controlledValue prop to true.

You will then be responsible for updating a local value state variable that is passed as the value prop to the DateRangeField.Root component.

	<script lang="ts">
	import { DateRangeField } from "bits-ui";
 
	let myValue = $state();
</script>
 
<DateRangeField.Root controlledValue value={myValue} onValueChange={(v) => (myValue = v)}>
	<!-- ... -->
</DateRangeField.Root>

See the Controlled State documentation for more information about controlled states.

API Reference

DateRangeField.Root

The root date field component.

Property Type Description
value $bindable
DateRange

The selected date range.

Default: undefined
onValueChange
function

A function that is called when the selected date changes.

Default: undefined
controlledValue
boolean

Whether or not the value is controlled or not. If true, the component will not update the value state internally, instead it will call onValueChange when it would have otherwise, and it is up to you to update the value prop that is passed to the component.

Default: false
placeholder $bindable
DateValue

The placeholder date, which is used to determine what date to start the segments from when no value exists.

Default: undefined
onPlaceholderChange
function

A function that is called when the placeholder date changes.

Default: undefined
controlledPlaceholder
boolean

Whether or not the placeholder is controlled or not. If true, the component will not update the placeholder state internally, instead it will call onPlaceholderChange when it would have otherwise, and it is up to you to update the value prop that is passed to the component.

Default: false
isDateUnavailable
function

A function that returns whether or not a date is unavailable.

Default: undefined
minValue
DateValue

The minimum valid date that can be entered.

Default: undefined
maxValue
DateValue

The maximum valid date that can be entered.

Default: undefined
granularity
enum

The granularity to use for formatting the field. Defaults to 'day' if a CalendarDate is provided, otherwise defaults to 'minute'. The field will render segments for each part of the date up to and including the specified granularity.

Default: undefined
hideTimeZone
boolean

Whether or not to hide the time zone segment of the field.

Default: false
hourCycle
enum

The hour cycle to use for formatting times. Defaults to the locale preference

Default: undefined
locale
string

The locale to use for formatting dates.

Default: 'en-US'
disabled
boolean

Whether or not the accordion is disabled.

Default: false
readonly
boolean

Whether or not the field is readonly.

Default: false
readonlySegments
EditableSegmentPart[]

An array of segments that should be readonly, which prevent user input on them.

Default: undefined
required
boolean

Whether or not the date field is required.

Default: false
onStartValueChange
function

A function that is called when the start date changes.

Default: undefined
onEndValueChange
function

A function that is called when the end date changes.

Default: undefined
ref $bindable
HTMLDivElement

The underlying DOM element being rendered. You can bind to this to get a reference to the element.

Default: undefined
children
Snippet

The children content to render.

Default: undefined
child
Snippet

Use render delegation to render your own element. See delegation docs for more information.

Default: undefined
Data Attribute Value Description
data-date-range-field-root
''

Present on the root element.

DateRangeField.Input

The container for the segments of the date field.

Property Type Description
type required
enum

The type of field to render (start or end).

Default: undefined
name
string

The name of the date field used for form submission. If provided, a hidden input element will be rendered alongside the date field.

Default: undefined
ref $bindable
HTMLDivElement

The underlying DOM element being rendered. You can bind to this to get a reference to the element.

Default: undefined
children
Snippet

The children content to render.

Default: undefined
child
Snippet

Use render delegation to render your own element. See delegation docs for more information.

Default: undefined
Data Attribute Value Description
data-invalid
''

Present on the element when the field is invalid.

data-disabled
''

Present on the element when the field is disabled.

data-date-field-input
''

Present on the element.

DateRangeField.Segment

A segment of the date field.

Property Type Description
part required
SegmentPart

The part of the date to render.

Default: undefined
ref $bindable
HTMLSpanElement

The underlying DOM element being rendered. You can bind to this to get a reference to the element.

Default: undefined
children
Snippet

The children content to render.

Default: undefined
child
Snippet

Use render delegation to render your own element. See delegation docs for more information.

Default: undefined
Data Attribute Value Description
data-invalid
''

Present on the element when the field is invalid

data-disabled
''

Present on the element when the field is disabled

data-segment
enum

The type of segment the element represents.

data-date-field-segment
''

Present on the element.

DateRangeField.Label

The label for the date field.

Property Type Description
ref $bindable
HTMLSpanElement

The underlying DOM element being rendered. You can bind to this to get a reference to the element.

Default: undefined
children
Snippet

The children content to render.

Default: undefined
child
Snippet

Use render delegation to render your own element. See delegation docs for more information.

Default: undefined
Data Attribute Value Description
data-invalid
''

Present on the element when the field is invalid

data-date-field-label
''

Present on the element.