# 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](https://vanilla-calendar.pro/) 3 in [AdminKit for Next.js](/nextjs/). 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:

```html
<div id="calendarInline"></div>

<script>
new VanillaCalendar.Calendar(document.querySelector("#calendarInline"), {
  selectedTheme: "system",
  themeAttrDetect: "html[data-bs-theme]",
  selectedDates: [new Date()],
  enableJumpToSelectedDate: true,
  firstWeekday: 0,
}).init();
</script>
```
```tsx code title="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

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `options` | `VanillaCalendarOptions` | | Vanilla Calendar Pro's options; read once, when the calendar is created |
| `input` | `boolean` | `false` | PRO: render a read-only `<input>` that opens the calendar as a popup (`inputMode`) |
| `time` | `boolean` | `false` | PRO: 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

> This feature is only available in **AdminKit PRO**. [Learn more](/pricing/#nextjs).

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:

```html
<div style="min-height: 22rem">
  <label class="form-label" for="datepickerDue">Due date</label>
  <input type="text" class="form-control" id="datepickerDue" placeholder="Select date.." readonly>
</div>

<script>
new VanillaCalendar.Calendar(document.querySelector("#datepickerDue"), {
  selectedTheme: "system",
  themeAttrDetect: "html[data-bs-theme]",
  inputMode: true,
  firstWeekday: 0,
  onChangeToInput(self) {
    if (self.context.inputElement) {
      self.context.inputElement.value = self.context.selectedDates[0] ?? "";
      self.hide();
    }
  },
}).init();
</script>
```
```tsx code title="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

> This feature is only available in **AdminKit PRO**. [Learn more](/pricing/#nextjs).

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

| Picker | Props and options |
| --- | --- |
| Date and time | `time`, `selectionTimeMode: 24`; write `` `${date} ${self.context.selectedTime}` `` |
| Several dates | `selectionDatesMode: "multiple"`; write `selectedDates.join(", ")` |
| Date range | `selectionDatesMode: "multiple-ranged"`; write the first and last of `selectedDates` |
| Time only | `time`, `selectionTimeMode: 24`, `selectionDatesMode: false`, `layouts: { default: "<#ControlTime />" }`; write `selectedTime` |

A time picker:

```tsx
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.

## Related

- [React Bootstrap datepicker](/react/docs/forms/date-picker/): the same component in AdminKit for React (Vite)
- [Bootstrap 5 datepicker](/bootstrap/docs/forms/date-picker/): the date picker of the HTML version
- [Calendar](/nextjs/docs/plugins/calendar/): the FullCalendar event calendar
- [Form controls](/nextjs/docs/forms/form-control/): inputs and submitting a form
- [Advanced Inputs demo](https://nextjs-demo.adminkit.io/forms-advanced-inputs)

---

Canonical HTML page: https://adminkit.io/nextjs/docs/forms/date-picker/

Site overview for agents: https://adminkit.io/llms.txt (expanded: https://adminkit.io/llms-full.txt) · Documentation: https://adminkit.io/bootstrap/docs/ · Questions: support@adminkit.io
