An easy-to-use date and time picker component built with vanilla JavaScript/TypeScript. Framework-agnostic and works with React, Vue, Angular, Svelte, or any other frontend framework.
- Simple select-based interface
- Multiple picker modes: Date, DateTime, Time
- Configurable return formats (Date object, ISO string, timestamp, date-string)
- Timezone-safe date handling (prevents off-by-one day issues)
- Fully typed with TypeScript
- Framework-agnostic (works with vanilla JS and all frameworks)
- Zero dependencies
- Responsive and mobile-friendly
- Dark mode support
- Customizable date ranges
- Custom styling support (Tailwind, Bootstrap, etc.)
npm install @kevinagyeman/easy-pickerimport EasyPicker from '@kevinagyeman/easy-picker'
import '@kevinagyeman/easy-picker/style.css'
// Create a date picker
const picker = new EasyPicker({
container: '#date-picker',
format: 'date',
initialDate: new Date(),
onChange: (date) => {
console.log('Selected date:', date)
}
})import { useEffect, useRef, useState } from 'react'
import EasyPicker from '@kevinagyeman/easy-picker'
import '@kevinagyeman/easy-picker/style.css'
function DatePickerComponent() {
const [date, setDate] = useState(new Date())
const containerRef = useRef<HTMLDivElement>(null)
const pickerRef = useRef<EasyPicker | null>(null)
useEffect(() => {
if (containerRef.current) {
pickerRef.current = new EasyPicker({
container: containerRef.current,
format: 'date',
initialDate: date,
onChange: setDate
})
}
return () => {
pickerRef.current?.destroy()
}
}, [])
// Sync picker when external state changes
useEffect(() => {
pickerRef.current?.update(date)
}, [date])
return (
<div>
<div ref={containerRef}></div>
<button onClick={() => setDate(new Date())}>Reset to Today</button>
</div>
)
}<template>
<div ref="pickerContainer"></div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import EasyPicker from '@kevinagyeman/easy-picker'
import '@kevinagyeman/easy-picker/style.css'
const pickerContainer = ref(null)
let picker = null
onMounted(() => {
picker = new EasyPicker({
container: pickerContainer.value,
format: 'date',
initialDate: new Date(),
onChange: (date) => {
console.log('Selected date:', date)
}
})
})
onUnmounted(() => {
picker?.destroy()
})
</script>import { Component, ElementRef, ViewChild, AfterViewInit, OnDestroy } from '@angular/core'
import EasyPicker from '@kevinagyeman/easy-picker'
import '@kevinagyeman/easy-picker/style.css'
@Component({
selector: 'app-date-picker',
template: '<div #pickerContainer></div>'
})
export class DatePickerComponent implements AfterViewInit, OnDestroy {
@ViewChild('pickerContainer') pickerContainer!: ElementRef
private picker?: EasyPicker
ngAfterViewInit() {
this.picker = new EasyPicker({
container: this.pickerContainer.nativeElement,
format: 'date',
initialDate: new Date(),
onChange: (date) => {
console.log('Selected date:', date)
}
})
}
ngOnDestroy() {
this.picker?.destroy()
}
}interface EasyPickerOptions {
// Container element or CSS selector
container: HTMLElement | string
// Initial date to display
initialDate?: Date
// Minimum selectable date
minDate?: Date
// Maximum selectable date
maxDate?: Date
// Callback fired when date changes
onChange?: (value: Date | string | number) => void
// Picker format: 'date', 'datetime', or 'time'
format?: 'date' | 'datetime' | 'time'
// Locale for month names (default: 'en-US')
locale?: string
// Custom CSS class for select elements (e.g., Tailwind classes)
selectClassName?: string
// Custom CSS class for wrapper elements
wrapperClassName?: string
// Return format for date values (default: 'date')
// 'date' - JavaScript Date object
// 'iso' - ISO 8601 string (e.g., "2025-03-15T12:00:00.000Z")
// 'timestamp' - Unix timestamp in milliseconds
// 'date-string' - Date-only string (e.g., "2025-03-15")
returnFormat?: 'date' | 'iso' | 'timestamp' | 'date-string'
}Returns the currently selected date in the format specified by returnFormat.
const currentDate = picker.getDate()
// Returns Date object by default
// Or ISO string, timestamp, or date-string based on returnFormat optionAlways returns the current date as a JavaScript Date object, regardless of returnFormat.
const rawDate = picker.getRawDate()
// Always returns a Date objectProgrammatically sets the picker to a specific date.
picker.setDate(new Date(2025, 5, 15))Updates the picker's date from external state (e.g., form state). Unlike setDate(), this is designed for syncing with external state and won't trigger onChange.
// Perfect for form integration
const formState = { date: new Date() };
picker.update(formState.date);Use update() when:
- Syncing with form/component state
- Resetting the picker
- Responding to external state changes
Use setDate() when:
- Programmatically changing the date
- Initial setup
Removes the picker from the DOM and cleans up event listeners.
picker.destroy()Shows month, day, and year columns.
new EasyPicker({
container: '#picker',
format: 'date'
})Shows month, day, year, hour, and minute columns.
new EasyPicker({
container: '#picker',
format: 'datetime'
})Shows hour and minute columns only.
new EasyPicker({
container: '#picker',
format: 'time'
})Control how date values are returned in the onChange callback and getDate() method:
new EasyPicker({
container: '#picker',
format: 'date',
returnFormat: 'date', // default
onChange: (date) => {
console.log(date) // Date object: Mon Mar 15 2025 12:00:00 GMT...
}
})new EasyPicker({
container: '#picker',
format: 'date',
returnFormat: 'iso',
onChange: (isoString) => {
console.log(isoString) // "2025-03-15T12:00:00.000Z"
// Perfect for APIs and databases
}
})new EasyPicker({
container: '#picker',
format: 'date',
returnFormat: 'timestamp',
onChange: (timestamp) => {
console.log(timestamp) // 1710504000000
// Number of milliseconds since Unix epoch
}
})new EasyPicker({
container: '#picker',
format: 'date',
returnFormat: 'date-string',
onChange: (dateString) => {
console.log(dateString) // "2025-03-15"
// Perfect for HTML input type="date" or simple date storage
}
})Note: When using format: 'date', dates are set to noon (12:00) to prevent timezone-related issues where the date shifts by one day when converted between timezones.
Pass custom classes via the selectClassName option:
// Tailwind CSS
new EasyPicker({
container: '#picker',
format: 'date',
selectClassName: 'px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500',
wrapperClassName: 'mx-2'
})
// Bootstrap
new EasyPicker({
container: '#picker',
format: 'date',
selectClassName: 'form-select',
wrapperClassName: 'mb-3'
})Override the default styles in your own CSS:
.easy-picker-select {
/* Your custom select styles */
border: 2px solid #0071e3;
border-radius: 12px;
}
.easy-picker-select:focus {
outline: none;
box-shadow: 0 0 0 4px rgba(0, 113, 227, 0.2);
}- Chrome/Edge (latest)
- Firefox (latest)
- Safari (latest)
- Mobile browsers (iOS Safari, Chrome Mobile)
# Install dependencies
npm install
# Run dev server with examples
npm run dev
# Build for production
npm run build
# Preview production build
npm run preview- π Fixed CSS import path (now use
@kevinagyeman/easy-picker/style.css)
- β¨ Added
returnFormatoption to choose output format (date, iso, timestamp, date-string) - β¨ Added
getRawDate()method to always get Date object - π Fixed timezone bug by setting date picker to noon (prevents day shifting)
- π Updated TypeScript types for
onChangecallback
- π Initial release
- Date, DateTime, and Time picker modes
- Framework-agnostic vanilla JS/TypeScript
- Custom styling support
- Dark mode support
MIT
Contributions are welcome! Please open an issue or submit a pull request.