Kontexte

Das Headless-Paket stellt Context-Provider zur Verfügung, die die Hooks umschließen und ihren Zustand über React Context verfügbar machen. Dies ermöglicht das Compound-Component-Pattern – teilen Sie Ihre Benutzeroberfläche in kleine, fokussierte Unterkomponenten auf, die den Zustand aus dem Kontext lesen, anstatt Props weiterzugeben.

Warum Kontexte?

Ohne Kontext müssten Sie jeden Teil des Zustands über Props durchreichen:

// Ohne Kontext – Prop Drilling
function MyDatePicker() {
const picker = useDatePicker({ value, onChange });
return (
<MyTrigger displayValue={picker.displayValue} onToggle={picker.handleToggle} />
<MyCalendar calendar={picker.calendar} getDayProps={picker.getDayProps} ... />
<MyFooter onConfirm={picker.handleConfirm} onCancel={picker.handleCancel} ... />
);
}

Mit Kontext konsumieren Unterkomponenten direkt, was sie benötigen:

// Mit Kontext – Compound Components
function MyDatePicker() {
return (
<DatePickerProvider value={value} onChange={onChange}>
<ExampleWrapper variant="headless" client:visible>
<MyTrigger client:visible />
</ExampleWrapper>
<ExampleWrapper variant="headless" client:visible>
<MyCalendar client:visible />
</ExampleWrapper>
<ExampleWrapper variant="headless" client:visible>
<MyFooter client:visible />
</ExampleWrapper>
</DatePickerProvider>
);
}
function MyTrigger() {
const { displayValue, handleToggle } = useDatePickerContext();
return <button onClick={handleToggle}>{displayValue}</button>;
}

Verfügbare Provider

ProviderIntern verwendeter HookConsumer-Hook
DatePickerProvideruseDatePickeruseDatePickerContext()
DateRangePickerProvideruseDateRangePickeruseDateRangePickerContext()
DateTimePickerProvideruseDateTimePickeruseDateTimePickerContext()
DateRangeTimePickerProvideruseDateRangeTimePickeruseDateRangeTimePickerContext()

Jeder Provider rendert sowohl einen typisierten Kontext (spezifisch für diesen Picker-Typ) als auch den vereinheitlichten PickerContext (für alle Typen gemeinsam).

import {
DatePickerProvider,
DateRangePickerProvider,
DateTimePickerProvider,
DateRangeTimePickerProvider,
} from "react-date-range-picker-headless";

DatePickerProvider

Umschließt useDatePicker. Stellt DatePickerContext + PickerContext zur Verfügung.

import type { DatePickerProviderProps } from "react-date-range-picker-headless";
PropTypBeschreibung
childrenReactNodeErforderlich. Kindkomponenten.
valueDate | nullErforderlich. Aktuelles Datum.
onChange(date: Date | null) => voidErforderlich. Change-Handler.
inlinebooleanInline rendern (kein Popup).
sizeDatePickerSizeGrößenvariante.
placeholderstringPlatzhalter für den Trigger.
namestringName des Formularfelds.
…restBaseDatePickerOptionsAlle Basisoptionen (minDate, maxDate, locale, etc.).

Consumer-Hook: useDatePickerContext() gibt UseDatePickerReturn zurück.

DateRangePickerProvider

Umschließt useDateRangePicker. Stellt DateRangePickerContext + PickerContext zur Verfügung.

import type { DateRangePickerProviderProps } from "react-date-range-picker-headless";
PropTypBeschreibung
childrenReactNodeErforderlich. Kindkomponenten.
value{ start: Date | null; end: Date | null }Erforderlich. Aktueller Bereich.
onChange(value: { start: Date | null; end: Date | null }) => voidErforderlich. Change-Handler.
inlinebooleanInline rendern.
sizeDatePickerSizeGrößenvariante.
placeholderstringPlatzhalter für den Trigger.
namestringName des Formularfelds.
maxDaysnumberMaximale Tage im Bereich.
minDaysnumberMinimale Tage im Bereich.
presetsDateRangePreset[]Bereichsvoreinstellungen.
allowSingleDateInRangebooleanEinzelnen Tag im Bereich erlauben.
…restBaseDatePickerOptionsAlle Basisoptionen.

Consumer-Hook: useDateRangePickerContext() gibt UseDateRangePickerReturn zurück.

DateTimePickerProvider

Umschließt useDateTimePicker. Stellt DateTimePickerContext + PickerContext zur Verfügung.

import type { DateTimePickerProviderProps } from "react-date-range-picker-headless";
PropTypBeschreibung
childrenReactNodeErforderlich. Kindkomponenten.
valueDate | nullErforderlich. Aktuelles Datum und Uhrzeit.
onChange(dateTime: Date | null) => voidErforderlich. Change-Handler.
timeTimeConfigZeitkonfiguration.
inlinebooleanInline rendern.
sizeDatePickerSizeGrößenvariante.
placeholderstringPlatzhalter für den Trigger.
namestringName des Formularfelds.
…restBaseDatePickerOptionsAlle Basisoptionen.

Consumer-Hook: useDateTimePickerContext() gibt UseDateTimePickerReturn zurück.

DateRangeTimePickerProvider

Umschließt useDateRangeTimePicker. Stellt DateRangeTimePickerContext + PickerContext zur Verfügung.

import type { DateRangeTimePickerProviderProps } from "react-date-range-picker-headless";
PropTypBeschreibung
childrenReactNodeErforderlich. Kindkomponenten.
value{ start: Date | null; end: Date | null }Erforderlich. Aktueller Bereich.
onChange(value: { start: Date | null; end: Date | null }) => voidErforderlich. Change-Handler.
timeTimeConfigZeitkonfiguration.
inlinebooleanInline rendern.
sizeDatePickerSizeGrößenvariante.
placeholderstringPlatzhalter für den Trigger.
namestringName des Formularfelds.
maxDaysnumberMaximale Tage im Bereich.
minDaysnumberMinimale Tage im Bereich.
presetsDateRangePreset[]Bereichsvoreinstellungen.
allowSingleDateInRangebooleanEinzelnen Tag im Bereich erlauben.
…restBaseDatePickerOptionsAlle Basisoptionen.

Consumer-Hook: useDateRangeTimePickerContext() gibt UseDateRangeTimePickerReturn zurück.

PickerContext (Vereinheitlicht)

Jeder Provider injiziert auch einen vereinheitlichten PickerContext. Dies ist ein Superset-Kontext, der alle Felder von jedem Picker-Typ enthält, mit optionalen Feldern für picker-spezifische Werte.

import { usePickerContext } from "react-date-range-picker-headless";
function MyGenericHeader() {
const { locale, handlePrevMonth, handleNextMonth, calendars } = usePickerContext();
return (
<div>
<button onClick={handlePrevMonth}>{locale.prevMonth}</button>
<span>{locale.formatMonthYear(calendars[0].month)}</span>
<button onClick={handleNextMonth}>{locale.nextMonth}</button>
</div>
);
}

Der vereinheitlichte Kontext ermöglicht es Ihnen, gemeinsame Unterkomponenten zu erstellen, die für alle Picker-Typen funktionieren. Die Styled-Pakete (styled, tailwind3, tailwind4) verwenden dieses Muster intern – ihre Header, Grid, Footer usw. Komponenten konsumieren alle den PickerContext.

PickerContext-Felder

import { PickerContext, usePickerContext } from "react-date-range-picker-headless";
import type { PickerContextValue } from "react-date-range-picker-headless";
function usePickerContext(): PickerContextValue;

Wirft einen Fehler, wenn es außerhalb eines Picker-Providers verwendet wird.

Kernfelder (immer vorhanden)

FeldTyp
isOpenboolean
localeLocale
focusedDateDate | null
displayValuestring
hasValueboolean
canConfirmboolean
calendarsCalendarMonth[]
getDayProps(date: Date, referenceMonth?: Date) => DayProps
handleToggle() => void
handleOpen() => void
handleClose() => void
handleConfirm() => void
handleCancel() => void
handleClear() => void
handleGoToToday() => void
handlePrevMonth() => void
handleNextMonth() => void
handleKeyDown(e: KeyboardEvent<HTMLElement>) => void
handleDateClick(date: Date) => void
containerRefRefObject<HTMLDivElement | null>
popupRefRefObject<HTMLDivElement | null>
yearsnumber[]
monthsnumber[]
handleYearSelect(year: number, calendarIndex?: number) => void
handleMonthSelect(month: number, calendarIndex?: number) => void

UI-Durchgangsfelder

FeldTyp
valueunknown
endNamestring | undefined
requiredboolean | undefined
inlineboolean | undefined
showOutsideDaysboolean | undefined
sizeDatePickerSize | undefined
placeholderstring | undefined
namestring | undefined
captionLayoutCaptionLayout | undefined

Bereichsspezifische Felder (optional)

FeldTyp
handleDateHover((date: Date | null) => void) | undefined
presetsDateRangePreset[] | undefined
handlePresetClick((preset: DateRangePreset) => void) | undefined
activePresetIndexnumber | undefined

Einzelne Zeitfelder (optional, DateTimePicker)

FeldTyp
resolvedTimeConfigRequired<TimeConfig> | undefined
tempHournumber | undefined
tempMinutenumber | undefined
tempSecondnumber | undefined
tempPeriodTimePeriod | undefined
handleHourChange((hour: number) => void) | undefined
handleMinuteChange((minute: number) => void) | undefined
handleSecondChange((second: number) => void) | undefined
handlePeriodChange((period: TimePeriod) => void) | undefined
isTimePickerOpenboolean | undefined
handleTimePickerOpen(() => void) | undefined
handleTimePickerClose(() => void) | undefined
handleTimePickerConfirm(() => void) | undefined
handleTimePickerCancel(() => void) | undefined
timePickerRefRefObject<HTMLDivElement | null> | undefined
timeDisplayValuestring | undefined

Bereichs-Zeitfelder (optional, DateRangeTimePicker)

FeldTyp
startHournumber | undefined
startMinutenumber | undefined
startSecondnumber | undefined
startPeriodTimePeriod | undefined
endHournumber | undefined
endMinutenumber | undefined
endSecondnumber | undefined
endPeriodTimePeriod | undefined
handleStartHourChange((hour: number) => void) | undefined
handleStartMinuteChange((minute: number) => void) | undefined
handleStartSecondChange((second: number) => void) | undefined
handleStartPeriodChange((period: TimePeriod) => void) | undefined
handleEndHourChange((hour: number) => void) | undefined
handleEndMinuteChange((minute: number) => void) | undefined
handleEndSecondChange((second: number) => void) | undefined
handleEndPeriodChange((period: TimePeriod) => void) | undefined
isStartTimePickerOpenboolean | undefined
isEndTimePickerOpenboolean | undefined
handleStartTimePickerOpen(() => void) | undefined
handleStartTimePickerClose(() => void) | undefined
handleStartTimePickerConfirm(() => void) | undefined
handleStartTimePickerCancel(() => void) | undefined
handleEndTimePickerOpen(() => void) | undefined
handleEndTimePickerClose(() => void) | undefined
handleEndTimePickerConfirm(() => void) | undefined
handleEndTimePickerCancel(() => void) | undefined
startTimePickerRefRefObject<HTMLDivElement | null> | undefined
endTimePickerRefRefObject<HTMLDivElement | null> | undefined
startTimeDisplayValuestring | undefined
endTimeDisplayValuestring | undefined

Typisierte vs. Vereinheitlichte Kontexte

AnwendungsfallWelcher Kontext
Erstellen einer gemeinsamen Komponente, die für alle Picker-Typen funktioniertusePickerContext()
Erstellen einer Komponente, die spezifisch für einen Picker ist, mit voller TypsicherheituseDatePickerContext(), useDateRangePickerContext(), etc.

Die typisierten Kontexte geben den exakten Rückgabetyp des Hooks zurück, sodass Sie eine vollständige TypeScript-Inferenz erhalten. Der vereinheitlichte Kontext verwendet optionale Felder für picker-spezifische Werte.

Dual-Kontext-Muster

Jeder Provider rendert zwei verschachtelte Kontexte:

<SpecificPickerContext.Provider value={hookReturn}>
<PickerContext.Provider value={unifiedValue}>{children}</PickerContext.Provider>
</SpecificPickerContext.Provider>
  • Verwenden Sie usePickerContext() für gemeinsame Unterkomponenten (funktioniert mit jedem Picker)
  • Verwenden Sie den typisierten Consumer-Hook für picker-spezifische Logik (volle Typsicherheit)

Beispiel: Zusammengesetzter Date Picker

import { useState } from "react";
import {
DatePickerProvider,
usePickerContext,
type DayProps,
} from "react-date-range-picker-headless";
/** A child component that reads from PickerContext instead of receiving props. */
function CalendarDisplay() {
const ctx = usePickerContext();
const calendar = ctx.calendars[0];
return (
<div style={{ border: "1px solid #ccc", padding: 16, borderRadius: 8, background: "#fff" }}>
{/* Header */}
<div style={{ display: "flex", justifyContent: "space-between", marginBottom: 8 }}>
<button onClick={ctx.handlePrevMonth}>←</button>
<span>{ctx.locale.formatMonthYear(calendar.month)}</span>
<button onClick={ctx.handleNextMonth}>→</button>
</div>
{/* Weekdays */}
<div style={{ display: "grid", gridTemplateColumns: "repeat(7, 1fr)", textAlign: "center" }}>
{ctx.locale.weekdays.map((wd) => (
<div key={wd} style={{ fontSize: 12, color: "#888", padding: 4 }}>
{wd}
</div>
))}
</div>
{/* Days */}
<div style={{ display: "grid", gridTemplateColumns: "repeat(7, 1fr)", textAlign: "center" }}>
{calendar.days.map((date, i) => {
if (!date) return <div key={i} />;
const dp: DayProps = ctx.getDayProps(date);
return (
<button
key={i}
onClick={() => ctx.handleDateClick(date)}
disabled={dp.isDisabled}
style={{
padding: 8,
cursor: dp.isDisabled ? "not-allowed" : "pointer",
background: dp.isSelected ? "#3b82f6" : "transparent",
color: dp.isSelected
? "#fff"
: dp.isToday
? "#3b82f6"
: dp.isDisabled
? "#ccc"
: "#333",
fontWeight: dp.isToday ? "bold" : "normal",
border: "none",
borderRadius: 4,
}}
>
{dp.day}
</button>
);
})}
</div>
{/* Footer */}
<div style={{ display: "flex", justifyContent: "flex-end", gap: 8, marginTop: 8 }}>
<button onClick={ctx.handleCancel}>Cancel</button>
<button onClick={ctx.handleConfirm} disabled={!ctx.canConfirm}>
Confirm
</button>
</div>
</div>
);
}
/** Main example wrapping with DatePickerProvider so children can use usePickerContext. */
function ContextExample() {
const [value, setValue] = useState<Date | null>(null);
return (
<div style={{ fontFamily: "system-ui" }}>
<DatePickerProvider value={value} onChange={setValue} initialOpen>
<div style={{ marginBottom: 8, fontSize: 14, color: "#555" }}>
Context-driven calendar (always open via initialOpen):
</div>
<CalendarDisplay />
</DatePickerProvider>
</div>
);
}
Context-driven calendar (always open via initialOpen):
March 2026
Su
Mo
Tu
We
Th
Fr
Sa