AdminKitDocs
Forms

Next.js Date Picker

A Next.js date picker for the App Router with Vanilla Calendar Pro: a client component for an inline calendar, and in AdminKit PRO a date and time input.

Use VanillaCalendar to show a calendar on a page, or to let people pick a date without typing it. It wraps Vanilla Calendar Pro 3 in AdminKit for Next.js. The free version draws an inline calendar, as on the Analytics dashboard. AdminKit PRO adds a date picker on a Bootstrap input, and a time picker.

VanillaCalendar is a client component, a file that starts with "use client", because the calendar is drawn in the browser. The HTML that Next.js renders on the server only has the empty <div>, or the empty input. Once the browser has taken over that HTML (hydration), a useEffect imports Vanilla Calendar Pro with a dynamic import() and draws the calendar. So the library never runs on the server.

The calendar follows the light or dark color mode. AdminKit passes selectedTheme: "system" and themeAttrDetect: "html[data-bs-theme]" before your options.

Example

This calendar selects today's date. It is a client component, so it works out that date in the browser, as the dashboard's calendar does:

src/components/today-calendar.tsx
"use client";

import { useState } from "react";
import { VanillaCalendar } from "@/components/plugins/vanilla-calendar";

export function TodayCalendar() {
  const [today] = useState(() => new Date());

  return (
    <VanillaCalendar
      options={{
        selectedDates: [today],
        enableJumpToSelectedDate: true,
        firstWeekday: 0,
      }}
    />
  );
}

A server component page could pass options={{ selectedDates: [new Date()] }} itself. With a static export, though, that code runs when the site is built, so the calendar would show the build date. Compute dates that depend on "now" in a client component, as above.

Props

PropTypeDefaultDescription
optionsVanillaCalendarOptionsVanilla Calendar Pro's options; read once, when the calendar is created
inputbooleanfalsePRO: render a read-only <input> that opens the calendar as a popup (inputMode)
timebooleanfalsePRO: add the time picker extension

Other props, such as className, id, placeholder and name, go to the <div>, or to the <input> with input. Later changes to options are ignored. Give the component a new key to create the calendar again. Options with callbacks, such as onChangeToInput, are functions. A server component cannot pass functions to the browser, so the component that defines them needs "use client". VanillaCalendar itself is in src/components/plugins/vanilla-calendar.tsx.

Date picker

Pro

This feature is only available in AdminKit PRO. Learn more.

When a form needs a date field, add input. It renders <input type="text" readOnly>, which opens the calendar as a popup. Write the input text yourself in onChangeToInput, as the demo's Datepickers client component does:

src/components/due-date-field.tsx
"use client";

import {
  VanillaCalendar,
  type VanillaCalendarOptions,
} from "@/components/plugins/vanilla-calendar";

const dueDate: VanillaCalendarOptions = {
  firstWeekday: 0,
  onChangeToInput(self) {
    if (self.context.inputElement) {
      self.context.inputElement.value = self.context.selectedDates[0] ?? "";
      self.hide();
    }
  },
};

export function DueDateField() {
  return (
    <>
      <label className="form-label" htmlFor="datepickerDue">Due date</label>
      <VanillaCalendar
        input
        id="datepickerDue"
        name="due"
        className="form-control"
        placeholder="Select date.."
        options={dueDate}
      />
    </>
  );
}

selectedDates holds "YYYY-MM-DD" strings. With a name, the text is part of the form's FormData. Send it to your API with fetch, because a static export has no Server Actions.

Ranges, multiple dates and time

Pro

This feature is only available in AdminKit PRO. Learn more.

For a range, several dates or a time, start from the same pattern. These are the other pickers of the Advanced Inputs demo:

PickerProps and options
Date and timetime, selectionTimeMode: 24; write `${date} ${self.context.selectedTime}`
Several datesselectionDatesMode: "multiple"; write selectedDates.join(", ")
Date rangeselectionDatesMode: "multiple-ranged"; write the first and last of selectedDates
Time onlytime, selectionTimeMode: 24, selectionDatesMode: false, layouts: { default: "<#ControlTime />" }; write selectedTime

A time picker:

const meetingTime: VanillaCalendarOptions = {
  selectionTimeMode: 24,
  selectionDatesMode: false,
  layouts: { default: "<#ControlTime />" },
  onChangeToInput(self) {
    if (self.context.inputElement) {
      self.context.inputElement.value = self.context.selectedTime;
    }
  },
};

<VanillaCalendar input time className="form-control" placeholder="Select time.." options={meetingTime} />

Accessibility

  • Pass an id to VanillaCalendar and point a <label htmlFor> at it.
  • The input is readOnly: dates are picked in the calendar. The browser skips constraint validation on read-only inputs, so required does nothing there. Check the value before you submit.
  • Tell users the format the input shows, in the placeholder or in help text.

Last updated on

On this page