vanilla-calendar-pro: a date picker you extend instead of configure
Lightweight, framework-agnostic date and time picker with TypeScript support and zero dependencies.
At a glance
- What is it?
- Vanilla Calendar Pro is a TypeScript date and time picker published to npm with no runtime dependencies, where optional features such as time, motion, weeks and annotations arrive as separate extensions and separate CSS files. It suits teams that want a calendar that matches their own design system rather than a framework component, and it costs an explicit extension registration step in v3.
- Who is it for?
- Adopt vanilla-calendar-pro when the calendar has to live outside a framework, carry your own CSS variables, and pick up time or range selection without a dependency tree. Do not adopt it for a simple native text field, and do not assume a drop-in upgrade from earlier versions, because the v3 extension registration changes the import shape.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 31 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A picker that ships no dependencies and no opinions
Vanilla Calendar Pro solves a narrow problem: a date, time and range picker that a product team can restyle completely, without pulling a UI framework into the application. The project describes itself as a flexible JavaScript date and time picker, with zero dependencies, TypeScript support, and a home in any framework. The audience is the front end developer who has inherited a design system and needs a calendar that obeys it, or who is building a booking flow where a stock component would arrive with its own opinions about corners, shadows and month headers.
The word vanilla in the name is about the integration surface rather than the feature set. The picker is not bare. It covers single dates, multiple dates and continuous ranges, month, multi-month and compact week views, week numbers, locale-aware labels, custom weekends and holidays, date restrictions, popups, range annotations and tooltips, a time picker with configurable limits and steps, dark and light themes, CSS variable customization, custom HTML layouts, swipe navigation, reduced-motion handling, keyboard navigation and screen reader labels. Independent instances are supported, so several calendars can share a page with their own settings and selections.
The project is not archived, and the last push was on 2026-09-22, which is the same day the v3.4.0 release was tagged. v3.3.2 landed on 2026-09-04 and v3.3.1 on 2026-08-23, so the release cadence around v3 is steady.
Extensions decide what your bundle carries
The architecture is a small core with named extensions attached to it. The core draws the month grid and handles selection. Everything beyond that arrives as an extension: time for the time picker, motion for animated navigation, annotations for date popups and range tooltips, weeks for the week features, and months for multi-month layouts. Each extension is passed into the constructor in the extensions array, and each has a matching stylesheet.
That split is the point of the design. The project states that you import only the features and CSS you need so that unused extensions stay out of your application bundle, and that unused features stay out means a booking form that needs only a date field does not ship the multi-month renderer, the swipe handler or the time picker. The cost is that the import list becomes part of your code, and that forgetting a stylesheet or an extension produces a calendar with a gap in it rather than an error.
The rendering model is a direct DOM construction. The constructor receives a selector such as '#calendar' and an options object, and init() performs the work. The tree at the top of the repository is a Vite and TypeScript monorepo: pnpm-workspace.yaml and pnpm-lock.yaml manage several packages, the package directory holds the published code, cypress/ and tests/ hold the test suites including an accessibility spec and a performance runner, demo/ and examples/ hold runnable pages, and biome.json drives the lint and format scripts. Shadow DOM is supported for Web Components, so the calendar can be embedded without leaking styles outward.
Installing the package and rendering the first month
The package is published to npm under the name vanilla-calendar-pro, and the project also points at a CDN build and a complete downloadable package for applications that prefer a script tag. The install is a single package manager command:
npm install vanilla-calendar-pro
# or: pnpm add vanilla-calendar-proThe markup the picker expects is minimal, a single container element:
<div id="calendar"></div>The JavaScript imports the Calendar class and the core stylesheet, then constructs the instance:
import { Calendar } from 'vanilla-calendar-pro';
import 'vanilla-calendar-pro/styles/core.css';
new Calendar('#calendar', {
selectionDatesMode: 'multiple-ranged',
}).init();The selectionDatesMode option in that snippet is the switch that turns the picker from a single date into a ranged multi-date selector. For a field-driven calendar the target is an input element instead, and the option that changes the behaviour is inputMode:
<input type="text" id="calendar-input" aria-label="Choose a date">new Calendar('#calendar-input', { inputMode: true }).init();The input carries an aria-label in the example, which is the accessibility floor for a text field that a calendar attaches to.
Time and motion as opt-in extensions
Adding a feature means adding a name to the extensions array and a stylesheet to the import list, in the same breath. The project illustrates this with the time picker and swipe navigation together:
import { Calendar, motion, time } from 'vanilla-calendar-pro';
import 'vanilla-calendar-pro/styles/core.css';
import 'vanilla-calendar-pro/styles/motion.css';
import 'vanilla-calendar-pro/styles/time.css';
new Calendar('#calendar', {
extensions: [motion, time],
animation: true,
enableSwipe: true,
selectionTimeMode: 24,
}).init();Three stylesheet imports for two extensions is the pattern to internalise. core.css carries the layout and both the light and dark themes, so it is the one import you cannot skip, while motion.css and time.css add the visuals for the features you registered. The other style files follow the same rule: styles/weeks.css for week features, styles/index.css for every feature and both themes at once, and individual layout and theme parts if you would rather keep only the theme you use.
The time picker itself is not a separate component bolted onto the grid. It attaches to the same selection, and selectionTimeMode takes 24 for a 24-hour clock, with limits, steps and control placement among the configurable options. The project also handles the week level directly, with a compact week view, clickable week numbers and weekday responses, plus a mode that collapses the calendar down to a week for narrow screens.
Availability rules and locales are options, not forks
Product calendars usually exist to express a rule set, and that is where this library puts its weight. Dates can be limited, individual weekdays disabled, specific dates or ranges blocked, or only certain days enabled. The result is a picker that refuses the choices your business does not allow, which is the difference between a date field and a booking widget.
Localisation is handled the same way, through configuration rather than a locale file you have to hand-edit. Labels can be localized, the first day of the week can be chosen, and weekends and holidays can be defined as your own sets, so a calendar that treats Friday and Saturday as the weekend in one market and Saturday and Sunday in another does not need a patched build. Week numbers can be shown, and clicks on a week or a weekday can be answered by your own code, which is the hook a dashboard needs for a per-week aggregate.
The context layer is where the calendar stops being a grid. Date popups, range annotations and selection tooltips attach your own content to a day, so a day can explain itself, mark a blackout, or carry a per-night price. The readme lists this as context for every date, and it is the part of the feature set that is hardest to imitate with a plain grid of buttons.
The v3 extension registration is the real upgrade cost
The limitation worth planning around is the migration. The project states that existing options stay the same, that ESM applications now need to import and register the extensions they use, and that existing full CSS imports still work. In practice that means an application built on the pre-v3 style of imports will not simply work: every feature that used to be present by default has to be named, imported, and pushed into the extensions array, and a missed entry shows up as a missing capability rather than a thrown error. CSS is the gentler half, because the complete styles/index.css path still resolves.
The styling model has the same tension. Replacing classes and rearranging the calendar's HTML layout are advertised as supported, and CSS variables handle colors, so full visual control is possible. What the project does not publish is a stability promise for the internal class names, so a heavily customised stylesheet that targets class names directly is taking a maintenance risk that a project depending only on the documented variables does not. The readme does not document a public theming API for the HTML structure, and it does not document a rollback path either.
On dates, the project states that date handling is tested across timezones and daylight-saving changes, and lists a browser floor of Chrome 57+, Firefox 52+, Edge 80+, Opera 44+ and Safari 10.1+. The stated support is real but old at the bottom end, which matters mainly for the Web Component and Shadow DOM path.
Where a framework date field fits better
The honest alternative is not another calendar library but a framework's own date component, or simply leaving the browser's own input in place. The difference in approach is the whole design. A framework component arrives already bound to the framework's rendering cycle, already themed with the framework's design tokens, and already constrained to the data patterns the framework expects, so it costs one import and little else. Vanilla Calendar Pro deliberately does none of that: it renders plain DOM, it offers no design of its own beyond two base themes, and it expects you to own the stylesheet, the locale, the date handling and now the extension list.
That trade pays off when the calendar has to appear in more than one framework, when the design system forbids a component that ships its own styles, or when the availability rules are specific enough that a generic field cannot express them. It costs you when the calendar is a single date field in a form, because then the extension registrations, the stylesheet imports and the theme files are overhead for a widget the browser can already draw.
A middle path exists inside the project itself. The CDN and complete downloadable package are offered for applications that prefer a script tag, and the styles/index.css import gives you every feature and both themes in one file. Those variants trade bundle discipline for setup simplicity, which is the same trade the extension system makes in the first place.
Editorial conclusion
Adopt vanilla-calendar-pro when the calendar has to live outside a framework, carry your own CSS variables, and pick up time or range selection without a dependency tree. Do not adopt it for a simple native text field, and do not assume a drop-in upgrade from earlier versions, because the v3 extension registration changes the import shape. Verify first that the extensions you need exist in the version you pin, that the CSS files you import match the features you enabled, and that the browser range you care about is covered by Chrome 57+, Firefox 52+, Edge 80+, Opera 44+ and Safari 10.1+.
Frequently asked questions
How do I install vanilla-calendar-pro?
The package is published to npm, so npm install vanilla-calendar-pro or pnpm add vanilla-calendar-pro is enough. The project also offers a CDN build and a complete downloadable package for applications that prefer a script tag.
How do I add the time picker to vanilla-calendar-pro?
Import the time extension and its stylesheet, then list it in the extensions array along with motion if you want animated navigation. Set selectionTimeMode to 24 for a 24-hour clock, and enable swipe with enableSwipe.
Does vanilla-calendar-pro work with React or other frameworks?
The project describes itself as framework-agnostic with zero dependencies, and the API is a plain constructor call on a selector followed by init(). The repository also ships a demo/ and examples/ directory for runnable pages.
What changed when upgrading to version 3 of vanilla-calendar-pro?
Existing options stay the same, but ESM applications now need to import and register the extensions they use. Existing full CSS imports still work, and migration details are published in the settings reference.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/uvarov-frontend-vanilla-calendar-pro)Was this useful?