npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@vakac995/react-calendar

v1.0.15

Published

A flexible, customizable React calendar component with date range and time picker support

Downloads

1,194

Readme

@vakac995/react-calendar

CI npm npm bundle size Coverage TypeScript License: MIT

A flexible, customizable React calendar component with date range and time picker support.

  • 📅 Single date and date range selection
  • ⏰ Integrated time picker with hours, minutes, seconds
  • 🎨 Fully customizable via classNames prop (works with Tailwind, CSS Modules, etc.)
  • 💪 Full TypeScript support
  • 📦 Tree-shakeable ES modules
  • 🪶 Zero dependencies (only React as peer dependency)

tl;dr

  • Install: npm install @vakac995/react-calendar
  • Import: import { Calendar } from '@vakac995/react-calendar'
  • Use: <Calendar value={value} onChange={setValue} />

Demo

📺 Live Demo — Try all features including range selection, time picker, and custom styling.

Installation

npm install @vakac995/react-calendar
yarn add @vakac995/react-calendar
pnpm add @vakac995/react-calendar

Compatibility

  • React 18.0.0 or later
  • TypeScript 5.0 or later (optional, but recommended)

Getting Started

Basic Usage

import { useState } from 'react';
import { Calendar, type DateTimeValue } from '@vakac995/react-calendar';

function App() {
  const [value, setValue] = useState<DateTimeValue | null>(null);

  return (
    <Calendar
      mode="single"
      value={value}
      onChange={setValue}
    />
  );
}

Date Range Selection

import { useState } from 'react';
import { Calendar, type DateRangeValue } from '@vakac995/react-calendar';

function App() {
  const [range, setRange] = useState<DateRangeValue | null>(null);

  return (
    <Calendar
      mode="range"
      value={range}
      onChange={setRange}
    />
  );
}

With Time Picker

<Calendar
  mode="single"
  value={value}
  onChange={setValue}
  showTime
  showSeconds
  timePosition="bottom"
/>

Time picker supports three positions:

  • "bottom" (default) — Time picker below the calendar
  • "top" — Time picker above the calendar
  • "side" — Time picker to the right of the calendar

Responsive Layout

The calendar automatically adapts to its container width using ResizeObserver:

<Calendar
  mode="range"
  value={value}
  onChange={setValue}
  showTime
  timePosition="side"
  layout="auto"        // Auto-detect based on container width
  mobileBreakpoint={420} // Switch to mobile below 420px
/>

Layout modes:

  • "auto" (default) — Automatically switches based on container width
  • "desktop" — Always use desktop layout
  • "mobile" — Always use mobile layout

On mobile layout:

  • timePosition="side" is automatically converted to "bottom"
  • Time pickers become collapsible accordions to save space

Date Constraints

<Calendar
  value={value}
  onChange={setValue}
  minDate={new Date(2024, 0, 1)}
  maxDate={new Date(2024, 11, 31)}
/>

Week Configuration

<Calendar
  value={value}
  onChange={setValue}
  weekStartsOn={1}      // Monday (0 = Sunday, 1 = Monday, etc.)
  showWeekNumbers       // Show ISO week numbers
/>

Styling

The calendar ships with minimal default styles and is designed to be customized. Every element can be styled via the classNames prop.

With Tailwind CSS

<Calendar
  value={value}
  onChange={setValue}
  classNames={{
    root: 'bg-white rounded-xl shadow-lg p-4',
    dayButton: 'w-10 h-10 rounded-full hover:bg-gray-100',
    daySelected: 'bg-blue-500 text-white hover:bg-blue-600',
    dayToday: 'border-2 border-blue-500',
    dayInRange: 'bg-blue-100',
  }}
/>

Extending Default Styles

By default, custom classNames replace the defaults. If you want to extend instead:

import { Calendar, extendClassNames, defaultClassNames } from '@vakac995/react-calendar';

<Calendar
  classNames={extendClassNames(defaultClassNames, {
    daySelected: 'ring-2 ring-offset-2', // Added to defaults
  })}
/>

Available Class Name Keys

| Key | Description | |-----|-------------| | root | Root container element | | rootDisabled | Root when calendar is disabled | | rootDefaultLayout | Root when timePosition is top/bottom | | rootSideLayout | Root when timePosition is side | | calendarWrapper | Wrapper around calendar grid | | calendarWrapperDisabled | Calendar wrapper when disabled |

| Key | Description | |-----|-------------| | header | Header container | | headerDisabled | Header when calendar is disabled | | headerNavigation | Navigation buttons container | | headerNavigationButton | All navigation buttons | | headerNavigationButtonDisabled | Navigation buttons when disabled | | headerNavigationButtonPrev | Previous buttons (year/month) | | headerNavigationButtonNext | Next buttons (year/month) | | headerTitle | Month/year title area | | headerMonthSelect | Month dropdown select | | headerMonthSelectDisabled | Month select when disabled | | headerYearSelect | Year dropdown select | | headerYearSelectDisabled | Year select when disabled |

| Key | Description | |-----|-------------| | weekDaysRow | Weekday labels row | | weekDayCell | Individual weekday label | | weekDayCellWeekend | Weekend weekday labels (Sat/Sun) | | weekNumberPlaceholder | Empty cell when showWeekNumbers |

| Key | Description | |-----|-------------| | body | Calendar body container | | week | Week row | | weekNumber | Week number cell wrapper | | weekNumberDisabled | Week number when disabled | | weekNumberCell | Week number button/text |

| Key | Description | |-----|-------------| | day | Day cell wrapper | | dayButton | Day button element | | dayToday | Today's date | | daySelected | Selected date(s) | | dayInRange | Dates within selected range | | dayRangeStart | First date of range | | dayRangeEnd | Last date of range | | dayDisabled | Disabled/unavailable dates | | dayOutsideMonth | Dates from prev/next month | | dayWeekend | Weekend dates (Sat/Sun) |

| Key | Description | |-----|-------------| | dayRangeBackground | Range highlight background | | dayRangeBackgroundStart | Range start background | | dayRangeBackgroundEnd | Range end background | | dayRangeBackgroundMiddle | Middle of range background | | dayRangeBackgroundFirstOfWeek | First day of week in range | | dayRangeBackgroundLastOfWeek | Last day of week in range |

| Key | Description | |-----|-------------| | timePickerWrapper | Time picker container | | timePickerWrapperTop | Time picker when position=top | | timePickerWrapperBottom | Time picker when position=bottom | | timePickerWrapperSide | Time picker when position=side | | timeContainer | Individual time section (start/end) | | timeContainerDisabled | Time container when disabled | | timeLabel | Time section label | | timeLabelDisabled | Time label when disabled | | timeSelectors | Hour/min/sec selectors container | | timeSelectorsDisabled | Selectors container when disabled | | timeSelector | Individual selector column | | timeSelectorDisabled | Selector column when disabled | | timeSelectorLabel | Selector label (HH/MM/SS) | | timeSelectorLabelDisabled | Selector label when disabled | | timeSelectorScroll | Scrollable area | | timeSelectorScrollDisabled | Scroll area when disabled | | timeSelectorItem | Time option item | | timeSelectorItemSelected | Selected time item | | timeSelectorItemDisabled | Disabled time item | | timeSeparator | Separator between selectors | | timeSeparatorDisabled | Separator when disabled |

| Key | Description | |-----|-------------| | timePickerCollapsed | Collapsible time picker wrapper | | timePickerCollapsedDisabled | Collapsible wrapper when disabled | | timePickerToggle | Toggle button for expanding/collapsing | | timePickerToggleDisabled | Toggle button when disabled | | timePickerToggleIcon | Icon in toggle button | | timePickerToggleIconDisabled | Toggle icon when disabled | | timePickerToggleText | Text in toggle button | | timePickerToggleTextDisabled | Toggle text when disabled | | timePickerContent | Collapsible content wrapper | | timePickerContentExpanded | Content when expanded |

See CalendarClassNames for the complete type definition.

Custom Rendering

Custom Day Renderer

<Calendar
  value={value}
  onChange={setValue}
  renderDay={(day, defaultRender) => {
    // Add a dot indicator for specific dates
    const hasEvent = events.some(e => isSameDay(e.date, day.date));
    
    return (
      <div className="relative">
        {defaultRender}
        {hasEvent && (
          <span className="absolute bottom-1 left-1/2 w-1 h-1 bg-red-500 rounded-full" />
        )}
      </div>
    );
  }}
/>

Custom Header

<Calendar
  value={value}
  onChange={setValue}
  renderHeader={({ currentMonth, currentYear, onPrevMonth, onNextMonth }) => (
    <div className="flex justify-between items-center p-4">
      <button onClick={onPrevMonth}>←</button>
      <span>{MONTHS[currentMonth]} {currentYear}</span>
      <button onClick={onNextMonth}>→</button>
    </div>
  )}
/>

API Reference

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | mode | 'single' \| 'range' | 'single' | Selection mode | | value | DateTimeValue \| DateRangeValue \| null | — | Controlled value | | defaultValue | DateTimeValue \| DateRangeValue \| null | — | Uncontrolled default | | onChange | (value) => void | — | Called when value changes | | showTime | boolean | false | Show time picker | | timePosition | 'bottom' \| 'top' \| 'side' | 'bottom' | Time picker position | | showSeconds | boolean | false | Show seconds selector | | layout | 'auto' \| 'desktop' \| 'mobile' | 'auto' | Responsive layout mode | | mobileBreakpoint | number | 420 | Container width (px) for mobile layout | | minDate | Date | — | Minimum selectable date | | maxDate | Date | — | Maximum selectable date | | minTime | TimeValue | — | Minimum selectable time | | maxTime | TimeValue | — | Maximum selectable time | | years | number[] | Last 100 years | Available years for year dropdown | | weekStartsOn | 0-6 | 0 | First day of week (0=Sun) | | showWeekNumbers | boolean | false | Show week numbers | | locale | string | — | Locale for formatting | | disabled | boolean | false | Disable the calendar | | classNames | CalendarClassNames | — | Custom class names (see Styling) | | labels | CalendarLabels | — | Custom labels for i18n (see below) | | renderDay | (day, defaultRender) => ReactNode | — | Custom day renderer | | renderHeader | (props) => ReactNode | — | Custom header renderer |

Event Handlers

| Handler | Type | Description | |---------|------|-------------| | onChange | (value: CalendarValue<TMode>) => void | Value changed | | onDayClick | (date: Date, event: MouseEvent) => void | Day clicked | | onWeekClick | (weekData: WeekData, event: MouseEvent) => void | Week number clicked | | onMonthSelect | (month: number, year: number) => void | Month selected from dropdown | | onYearChange | (year: number) => void | Year changed from dropdown | | onPrevMonth | (month: number, year: number) => void | Navigate to previous month | | onNextMonth | (month: number, year: number) => void | Navigate to next month | | onPrevYear | (year: number) => void | Navigate to previous year | | onNextYear | (year: number) => void | Navigate to next year | | onTimeChange | (time: TimeValue, target: 'start' \| 'end' \| 'single') => void | Time changed | | onHourClick | (hour: number, target: 'start' \| 'end' \| 'single') => void | Hour clicked | | onMinuteClick | (minute: number, target: 'start' \| 'end' \| 'single') => void | Minute clicked |

Types

import type {
  // Props
  CalendarProps,
  CalendarClassNames,
  CalendarLabels,
  HeaderRenderProps,
  
  // Values
  CalendarValue,
  DateTimeValue,
  DateRangeValue,
  TimeValue,
  
  // Data
  DayCell,
  WeekData,
  MonthData,
  
  // Config
  SelectionMode,
  TimePosition,
  LayoutMode,
  DayOfWeek,
} from '@vakac995/react-calendar';

Key Types Explained

// Single date with optional time
interface DateTimeValue {
  date: Date;
  time?: TimeValue;
}

// Date range
interface DateRangeValue {
  start: DateTimeValue | null;
  end: DateTimeValue | null;
}

// Time value
interface TimeValue {
  hours: number;   // 0-23
  minutes: number; // 0-59
  seconds: number; // 0-59
}

// Day cell data (for renderDay)
interface DayCell {
  date: Date;
  isCurrentMonth: boolean;
  isToday: boolean;
  isSelected: boolean;
  isInRange: boolean;
  isRangeStart: boolean;
  isRangeEnd: boolean;
  isDisabled: boolean;
  weekNumber: number;
}

Utilities

The library exports utility functions for date manipulation:

import {
  isSameDay,
  addDays,
  addMonths,
  startOfDay,
  getWeekNumber,
  isDateInRange,
  isDateDisabled,
  getMonthData,
} from '@vakac995/react-calendar';

// Examples
isSameDay(date1, date2);           // Check if same day
addDays(date, 7);                   // Add days to date
addMonths(date, 1);                 // Add months to date
getWeekNumber(date);                // Get ISO week number
isDateInRange(date, start, end);    // Check if date in range

Default Exports

import {
  // Components
  Calendar,
  TimePicker,
  TimeSelector,
  
  // Styling utilities
  defaultClassNames,
  mergeClassNames,    // Replace defaults with custom
  extendClassNames,   // Extend defaults with custom
  defaultLabels,
  mergeLabels,
  
  // Constants
  DAYS_IN_WEEK,       // 7
  MONTHS,             // ['January', 'February', ...]
  SHORT_DAYS,         // ['Sun', 'Mon', ...]
} from '@vakac995/react-calendar';

Internationalization (i18n)

All text in the calendar can be customized via the labels prop. Labels are optional — sensible English defaults are provided.

Labels Reference

| Key | Default | Description | |-----|---------|-------------| | previousYear | "Previous year" | Aria-label for prev year button | | previousMonth | "Previous month" | Aria-label for prev month button | | nextMonth | "Next month" | Aria-label for next month button | | nextYear | "Next year" | Aria-label for next year button |

| Key | Default | Description | |-----|---------|-------------| | previousYearIcon | « SVG | Previous year button content | | previousMonthIcon | SVG | Previous month button content | | nextMonthIcon | SVG | Next month button content | | nextYearIcon | » SVG | Next year button content |

| Key | Default | Description | |-----|---------|-------------| | timeLabel | "Time" | Single mode time label | | startTimeLabel | "Start Time" | Range mode start time label | | endTimeLabel | "End Time" | Range mode end time label | | hoursLabel | "HH" | Hours column header | | minutesLabel | "MM" | Minutes column header | | secondsLabel | "SS" | Seconds column header |

| Key | Default | Description | |-----|---------|-------------| | months | ["January", "February", ...] | Array of 12 month names | | shortDays | ["Sun", "Mon", ...] | Array of 7 short day names (starting Sunday) |

Example: French Localization

<Calendar
  value={value}
  onChange={setValue}
  labels={{
    // Month names
    months: [
      'Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
      'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'
    ],
    // Day names (starting from Sunday)
    shortDays: ['Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam'],
    // Navigation
    previousYear: 'Année précédente',
    previousMonth: 'Mois précédent',
    nextMonth: 'Mois suivant',
    nextYear: 'Année suivante',
    // Time picker
    timeLabel: 'Heure',
    startTimeLabel: 'Heure de début',
    endTimeLabel: 'Heure de fin',
  }}
  weekStartsOn={1} // Monday first
/>

Using mergeLabels Utility

import { Calendar, defaultLabels, mergeLabels } from '@vakac995/react-calendar';

// Partially override defaults
const frenchLabels = mergeLabels(defaultLabels, {
  months: ['Janvier', 'Février', 'Mars', /* ... */],
});

<Calendar labels={frenchLabels} />

TypeScript

The calendar is fully typed. Generic type inference works based on mode:

// mode="single" → value is DateTimeValue | null
const [single, setSingle] = useState<DateTimeValue | null>(null);
<Calendar mode="single" value={single} onChange={setSingle} />

// mode="range" → value is DateRangeValue | null  
const [range, setRange] = useState<DateRangeValue | null>(null);
<Calendar mode="range" value={range} onChange={setRange} />

Browser Support

Works in all modern browsers that support ES2020+:

  • Chrome/Edge 80+
  • Firefox 78+
  • Safari 14+

Contributing

# Clone the repo
git clone https://github.com/vakac995/react-calendar.git
cd react-calendar

# Install dependencies
npm install

# Run development server
npm run dev

# Run linter
npm run lint

# Type check
npm run typecheck

# Build library
npm run build

License

MIT © vakac995