Vanilla JS
Try the live Vanilla JS demo for interactive picker and copy-paste snippets.
Support tier
Full support. Imperative mount API with no React peers required. Handles
expose update, destroy, and
getController.
Install + deps
npm install universal-datetime-picker
Only dayjs ships as a dependency. React peers stay optional.
Setup
Pick a DOM node as the mount target. Call
createDateTimePicker or
createDateTimeRangePicker with options that match the React
prop names. Import the shared stylesheet once.
Exact import path
import {
createDateTimePicker,
createDateTimeRangePicker,
} from "universal-datetime-picker/vanilla";
import "universal-datetime-picker/style.css"; Minimal example
import { createDateTimePicker } from "universal-datetime-picker/vanilla";
import "universal-datetime-picker/style.css";
const root = document.getElementById("picker")!;
const handle = createDateTimePicker(root, {
inline: true,
mode: "date",
asString: false,
onChange: (value) => console.log(value),
});
// later:
// handle.update({ open: false });
// handle.getController();
// handle.destroy(); Realistic example
Popover trigger pattern. Keep the picker closed until the button is
clicked by setting defaultOpen: false, then open with
update.
import { createDateTimePicker } from "universal-datetime-picker/vanilla";
import "universal-datetime-picker/style.css";
const trigger = document.getElementById("open-picker") as HTMLButtonElement;
const mount = document.getElementById("picker-host")!;
const handle = createDateTimePicker(mount, {
mode: "datetime",
asString: false,
popover: true,
anchorEl: trigger,
defaultOpen: false,
onChange: (value) => {
trigger.textContent =
value instanceof Date ? value.toLocaleString() : "Pick date & time";
},
onOpenChange: (open) => {
trigger.setAttribute("aria-expanded", String(open));
},
});
trigger.addEventListener("click", () => {
handle.update({ open: true, anchorEl: trigger });
}); Configuration notes
-
Option names match React props:
mode,asString,inline,popover,anchorEl,locale, and so on. -
update(partial)merges options and re-renders.destroy()unsubscribes and removes DOM.getController()returns the headless controller. -
Without
inline, omitpopoverfor a centered modal overlay. -
createDateTimeRangePickeruses the same handle shape. Range pickers do not takepopover/anchorEl. -
Non-inline defaults to open. Use
defaultOpen: falseor controlledopenfor trigger UIs.
Events
-
onChange: selected value callback on the options object -
onOpenChange: open state callback on the options object
There is no DOM CustomEvent on the vanilla handle. Wire callbacks in
options, or subscribe through getController().
Customization links
- Theming
- Locales
- Overlay and triggers
- Props (same option names)
Pitfalls
-
popover: truewithout a validanchorElmispositions or fails to open usefully. -
Leaving
defaultOpenat the default opens the picker as soon as you mount a non-inline instance. -
Calling
destroy()and thenupdate()on the same handle is invalid. Create a new picker. -
Importing from the React main entry pulls React components you do not
need. Use
/vanilla.
Troubleshooting
Missing CSS, wrong entry, or a popover without an anchor are the usual failures. See Troubleshooting.