universal-datetime-picker

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, omit popover for a centered modal overlay.
  • createDateTimeRangePicker uses the same handle shape. Range pickers do not take popover / anchorEl.
  • Non-inline defaults to open. Use defaultOpen: false or controlled open for 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

Pitfalls

  • popover: true without a valid anchorEl mispositions or fails to open usefully.
  • Leaving defaultOpen at the default opens the picker as soon as you mount a non-inline instance.
  • Calling destroy() and then update() 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.

Related links

Vanilla live demo · Examples · Props · Troubleshooting