Time Range Field

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

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

Overview

The TimeRangeField component combines two Time Field components to create a time range field. Check out the Time Field component documentation for information on how to customize this component.

Structure

	<script lang="ts">
  import { TimeRangeField } from "bits-ui";
</script>
 
<TimeRangeField.Root>
  <TimeRangeField.Label>Working Hours</TimeRangeField.Label>
  {#each ["start", "end"] as const as type}
    <TimeRangeField.Input {type}>
      {#snippet children({ segments })}
        {#each segments as { part, value }}
          <TimeRangeField.Segment {part}>
            {value}
          </TimeRangeField.Segment>
        {/each}
      {/snippet}
    </TimeRangeField.Input>
  {/each}
</TimeRangeField.Root>

Managing Placeholder State

This section covers how to manage the placeholder state of the component.

Two-Way Binding

Use bind:placeholder for simple, automatic state synchronization:

	<script lang="ts">
  import { TimeRangeField } from "bits-ui";
  import { Time } from "@internationalized/date";
  let myPlaceholder = $state(new Time(12, 30));
</script>
 
<TimeRangeField.Root bind:placeholder={myPlaceholder}>
  <!-- ... -->
</TimeRangeField.Root>

Fully Controlled

Use a Function Binding for complete control over the state's reads and writes.

	<script lang="ts">
  import { TimeRangeField, type TimeValue } from "bits-ui";
  import { Time } from "@internationalized/date";
  let myPlaceholder = $state(new Time(12, 30));
 
  function getPlaceholder() {
    return myPlaceholder;
  }
 
  function setPlaceholder(newPlaceholder: TimeValue) {
    myPlaceholder = newPlaceholder;
  }
</script>
 
<TimeRangeField.Root bind:placeholder={getPlaceholder, setPlaceholder}>
  <!-- ... -->
</TimeRangeField.Root>

Managing Value State

This section covers how to manage the value state of the component.

Two-Way Binding

Use bind:value for simple, automatic state synchronization:

	<script lang="ts">
  import { TimeRangeField, type TimeRange } from "bits-ui";
  import { Time } from "@internationalized/date";
  let myValue = $state<TimeRange>({
    start: new Time(12, 30),
    end: new Time(12, 30),
  });
</script>
 
<button
  onclick={() => {
    myValue = {
      start: myValue.start.add({ hours: 1 }),
      end: myValue.end.add({ hours: 1 }),
    };
  }}
>
  Add 1 hour
</button>
<TimeRangeField.Root bind:value={myValue}>
  <!-- ... -->
</TimeRangeField.Root>

Fully Controlled

Use a Function Binding for complete control over the state's reads and writes.

	<script lang="ts">
  import { TimeRangeField, type TimeRange } from "bits-ui";
 
  let myValue = $state<TimeRange | undefined>({
    start: undefined,
    end: undefined,
  });
 
  function getValue() {
    return myValue;
  }
 
  function setValue(newValue: TimeRange | undefined) {
    myValue = newValue;
  }
</script>
 
<DateRangeField.Root bind:value={getValue, setValue}>
  <!-- ... -->
</DateRangeField.Root>

API Reference

TimeRangeField.Root

The root time field component.

Property Details
value
onValueChange
placeholder
onPlaceholderChange
errorMessageId
validate
onInvalid
minValue
maxValue
granularity
hideTimeZone
hourCycle
locale
disabled
readonly
readonlySegments
required
onStartValueChange
onEndValueChange
ref
children
child
Data Attribute Details
data-time-range-field-root

TimeRangeField.Input

The container for the segments of the time field.

Property Details
type
name
ref
children
child
Data Attribute Details
data-invalid
data-disabled
data-time-field-input

TimeRangeField.Segment

A segment of the time field.

Property Details
part
ref
children
child
Data Attribute Details
data-invalid
data-disabled
data-segment
data-time-field-segment

TimeRangeField.Label

The label for the time field.

Property Details
ref
children
child
Data Attribute Details
data-invalid
data-time-field-label