Overlay and triggers
Presentation controls whether the calendar sits in the page flow or floats. Open-state props control when it is visible.
Presentation modes
| Mode | How to enable | Behavior |
|---|---|---|
| Inline | inline | Calendar stays in layout. Always "open". No backdrop. |
| Overlay (modal) |
Default when not inline, and popover is not set
| Centered panel with dimmed backdrop. Escape and outside click close it. |
| Popover | popover + anchorEl on
DateTime |
Positions near the anchor with
position: fixed, flips when needed, repositions on
scroll/resize.
|
DateTimeInput is always popover + its own input as anchor.
DateTimeRange has no popover or anchorEl. Use
inline or the centered overlay only.
Open-state props
| Prop | Type | Effect |
|---|---|---|
open | boolean | Controlled open state |
defaultOpen | boolean | Uncontrolled initial open |
onOpenChange | (open: boolean) => void | Fires when open state changes |
defaultOpen is true for non-inline DateTime
For DateTime (and DateTimeRange) when
inline is false, defaultOpen defaults to
true. The overlay appears on mount. That surprises
people building a closed custom button. Fix it with either:
-
defaultOpen={false}and open viaonOpenChange/ a button, or -
fully controlled
open+onOpenChange.
DateTimeInput defaults to closed because the field is the
trigger.
React: DateTimeInput (built-in trigger)
import { useState } from "react";
import { DateTimeInput } from "universal-datetime-picker";
import "universal-datetime-picker/style.css";
export function FormDate() {
const [value, setValue] = useState<Date | null>(null);
return (
<DateTimeInput
mode="date"
asString={false}
value={value}
onChange={(next) => setValue(next instanceof Date ? next : null)}
placeholder="Choose a date"
/>
);
} React: custom button with popover
import { useState } from "react";
import { DateTime } from "universal-datetime-picker";
import "universal-datetime-picker/style.css";
function CustomDateTrigger() {
const [open, setOpen] = useState(false);
const [value, setValue] = useState<Date | null>(null);
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
return (
<>
<button
ref={setAnchorEl}
type="button"
onClick={() => setOpen(true)}
aria-haspopup="dialog"
aria-expanded={open}
>
{value ? value.toLocaleDateString() : "Choose a date"}
</button>
<DateTime
mode="date"
open={open}
onOpenChange={setOpen}
popover
anchorEl={anchorEl}
asString={false}
value={value}
onChange={(next) => setValue(next instanceof Date ? next : null)}
/>
</>
);
}
Omit popover and anchorEl for a centered
modal overlay. Keep open controlled the same way.
Vanilla: popover + anchor + closed by default
import { createDateTimePicker } from "universal-datetime-picker/vanilla";
import "universal-datetime-picker/style.css";
const mount = document.querySelector("#picker-root");
const trigger = document.querySelector("#open-picker");
if (!mount || !trigger) throw new Error("missing mount or trigger");
const picker = createDateTimePicker(mount, {
mode: "date",
asString: false,
popover: true,
anchorEl: trigger,
defaultOpen: false,
onChange: (value) => console.log(value),
onOpenChange: (isOpen) => {
trigger.setAttribute("aria-expanded", String(isOpen));
},
});
trigger.addEventListener("click", () => {
picker.update({ open: true });
});
// Later: picker.destroy();
The handle exposes update, destroy, and
getController. Pass defaultOpen: false,
popover: true, and anchorEl, then open with
update({ open: true }).
Web Components note
For non-inline custom elements, an absent open attribute
means closed. That differs from React
DateTime's defaultOpen=true. Set the
open attribute (or toggle it from script) when you want
the overlay visible. Listen for openchange to sync your
trigger button.
<button id="btn" type="button">Pick date</button>
<datetime-picker id="dp" mode="date"></datetime-picker>
<script>
const dp = document.getElementById("dp");
const btn = document.getElementById("btn");
btn.addEventListener("click", () => dp.setAttribute("open", ""));
dp.addEventListener("openchange", (e) => {
if (!e.detail) dp.removeAttribute("open");
});
</script> Related: Components · Date range · Vanilla · Web Components · Examples · Troubleshooting