
Syncfusion React Calendars
- 205 installs
- 3 repo stars
- Updated July 28, 2026
- syncfusion/react-ui-components-skills
Guides implementing Syncfusion React Calendar, DatePicker, DateRangePicker, DateTimePicker, and TimePicker with data binding, formatting, localization, and validation.
About
Provides implementation guidance for Syncfusion React calendar and date/time picker components covering installation, binding, range selection, localization, and accessibility. A developer uses it when adding date and time input controls to a React app.
- Covers Calendar, DatePicker, DateRangePicker, DateTimePicker, and TimePicker
- Includes masking, validation, templates, and WCAG 2.2 accessibility
Syncfusion React Calendars by the numbers
- 205 all-time installs (skills.sh)
- +27 installs in the week ending Jul 27, 2026 (Skillselion tracking)
- Ranked #846 of 2,245 Frontend Development skills by installs in the Skillselion catalog
- Data as of Jul 29, 2026 (Skillselion catalog sync)
npx skills add https://github.com/syncfusion/react-ui-components-skills --skill syncfusion-react-calendarsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 205 |
|---|---|
| repo stars | ★ 3 |
| Last updated | July 28, 2026 |
| Repository | syncfusion/react-ui-components-skills ↗ |
What it does
Guides implementing Syncfusion React Calendar, DatePicker, DateRangePicker, DateTimePicker, and TimePicker with data binding, formatting, localization, and validation.
Files
Implementing Syncfusion React Calendars
Calendar
The Syncfusion React CalendarComponent is a highly customizable calendar UI control that allows users to select single or multiple dates. It supports multiple views (Month, Year, Decade), navigation, week numbers, disabled dates, custom day cell rendering, localization, RTL support, and full accessibility (WCAG 2.2 compliant).
Quick Start (React)
Install
npm install @syncfusion/ej2-react-calendars @syncfusion/ej2-baseBasic Example (App.jsx)
import React, { useState } from 'react';
import { CalendarComponent } from '@syncfusion/ej2-react-calendars';
import '@syncfusion/ej2-base/styles/material3.css';
import '@syncfusion/ej2-calendars/styles/material3.css';
export default function App() {
const [value, setValue] = useState(new Date());
const onChange = (args) => setValue(args.value || args);
return (
<div style={{ padding: 20 }}>
<h3>Select a date</h3>
<CalendarComponent value={value} change={onChange} />
<p>Selected: {value.toDateString()}</p>
</div>
);
}Notes:
- Use the
changeevent to sync selected date to React state. - Import theme CSS once (global or component-level) to style the control.
Guidance & Patterns
- Controlled component: keep source-of-truth in React state and update
valueviachangeevent. - Multi-selection: use
isMultiSelection={true}withvaluesprop andaddDate()/removeDate()methods. - Programmatic navigation: use a
refto callnavigateTo(view, date)— both arguments are required (see references/getting-started-react.md). - Date ranges: for range selection, use DateRangePicker (separate component). The Calendar itself does not have a built-in range highlight mode.
- Accessibility: use wrapper elements with
role="region"and a separatearia-liveregion for announcements — these are not direct Calendar props. - Week numbers: enable with
weekNumber={true}(the correct prop name).
References
Navigate to the reference that matches your current task:
Getting Started
📄 Read: references/getting-started-react.md
- Installation and npm setup
- React component examples
- CSS/theme imports
- Using refs and methods
Date Selection
📄 Read: references/date-selection.md
- Single date selection
- Multiple dates and ranges
- Min/max constraints
- Disabling specific dates
Calendar Views
📄 Read: references/calendar-views.md
- Month, Year, Decade views
- Navigating between views
- Initial and depth controls
- Programmatic navigation
Styling & Customization
📄 Read: references/styling-customization.md
- Theme selection and switching
- CSS class customization
- Custom day cell rendering
- RTL and responsive design
Events & Methods
📄 Read: references/events-methods.md
- Event handlers (change, created, renderDayCell)
- Using refs and imperative methods
- Advanced renderDayCell hook
- Event tracking patterns
Accessibility & Globalization
📄 Read: references/accessibility-globalization.md
- WCAG 2.1 compliance
- Keyboard navigation
- ARIA attributes
- Locale support and RTL
- Testing for accessibility
API Reference (Quick Lookup)
📄 Read: references/api-reference.md
- Props, events, methods at a glance
- Common enums and types
- Link to upstream docs
Troubleshooting & Tips
- Styles not applied: confirm CSS imports point to
node_modules/@syncfusion/ej2-calendars/styles/and are loaded before component styles. - React state mismatch: use the
valueprop andchangeevent to keep React state in sync — do not rely on framework-specific bindings. - Multiple date selection not working: ensure
isMultiSelection={true}and usevalues(notvalue) for the initial array. - `navigateTo` not working: the method requires two arguments —
navigateTo(view: CalendarView, date: Date). - "Cannot find module": run
npm install @syncfusion/ej2-react-calendars @syncfusion/ej2-baseand confirmpackage.json. - Week numbers not showing: use
weekNumber={true}(notshowWeekNumber).
DatePicker
The Syncfusion React DatePickerComponent provides an intuitive input control with a calendar popup for selecting a single date. It features flexible formatting, masked input, min/max date validation, strict mode, multiple input formats, custom day rendering, localization, and seamless integration as a controlled React component.
Component Overview
The DatePicker is a Syncfusion React component for date selection with powerful features:
- Calendar popup - Visual date selection with navigation
- Flexible formatting - Display and input formats with pattern support
- Masked input -
enableMaskfor segment-by-segment date entry withmaskPlaceholder - Range validation - Min/max dates with
strictModeautomatic correction - Multiple views - Month, year, and decade views via
startanddepthproperties - Day cell customization - Disable weekends, highlight special dates via
renderDayCellevent - Full globalization - 150+ cultures, RTL (
enableRtl), locale-specific formatting,firstDayOfWeek - WCAG 2.2 compliant - Full accessibility with keyboard navigation and ARIA attributes
- Form ready - Controlled components, React hooks, form validation integration
- Programmatic control -
show(),hide(),focusIn(),focusOut(),navigateTo(),currentView()
Complete API Summary
Key Properties
| Property | Type | Default | Description |
|---|---|---|---|
value | Date | null | Selected date |
min | Date | 1900-01-01 | Minimum selectable date |
max | Date | 2099-12-31 | Maximum selectable date |
format | string \ | FormatObject | null |
inputFormats | string[] \ | FormatObject[] | null |
placeholder | string | null | Placeholder text for the input |
enabled | boolean | true | Enable or disable the component |
readonly | boolean | false | Readonly state |
allowEdit | boolean | true | Allow editing the input textbox |
strictMode | boolean | false | Auto-correct out-of-range dates |
showClearButton | boolean | true | Show/hide the clear button |
showTodayButton | boolean | true | Show/hide today button |
start | CalendarView | Month | Initial view: "Month", "Year", "Decade" |
depth | CalendarView | Month | Deepest navigation level |
enableMask | boolean | false | Enable masked date input |
maskPlaceholder | MaskPlaceholderModel | {...} | Segment placeholders for masked input |
enableRtl | boolean | false | Right-to-left rendering |
locale | string | '' | Culture/locale code |
firstDayOfWeek | number | 0 | First day of week (0=Sunday) |
weekNumber | boolean | false | Show week numbers |
weekRule | WeekRule | FirstDay | Rule for first week of year |
calendarMode | CalendarType | Gregorian | Calendar type (Gregorian or Islamic) |
dayHeaderFormat | DayHeaderFormats | Short | Day name format in header |
floatLabelType | FloatLabelType | Never | Floating label behavior |
fullScreenMode | boolean | false | Full screen popup on mobile |
openOnFocus | boolean | false | Open popup on input focus |
serverTimezoneOffset | number | null | Server timezone offset |
cssClass | string | null | Custom CSS class |
htmlAttributes | { [key: string]: string } | {} | Additional HTML attributes |
keyConfigs | { [key: string]: string } | null | Custom key action mappings |
width | number \ | string | null |
zIndex | number | 1000 | Popup z-index |
enablePersistence | boolean | false | Persist state between reloads |
Methods
| Method | Returns | Description |
|---|---|---|
show() | void | Opens the calendar popup |
hide() | void | Closes the calendar popup |
focusIn() | void | Sets focus to the component |
focusOut() | void | Removes focus from the component |
navigateTo(view, date) | void | Navigates to a specific view and date |
currentView() | string | Returns the current calendar view name |
getPersistData() | string | Gets persisted state data |
removeDate(dates) | void | Removes date(s) from the values |
destroy() | void | Destroys the component |
Events
| Event | Args Type | Description |
|---|---|---|
change | ChangedEventArgs | Fires when the selected date changes |
focus | FocusEventArgs | Fires when input gains focus |
blur | BlurEventArgs | Fires when input loses focus |
open | PreventableEventArgs \ | PopupObjectArgs |
close | PreventableEventArgs \ | PopupObjectArgs |
cleared | ClearedEventArgs | Fires when value is cleared |
created | Object | Fires when component is created |
destroyed | Object | Fires when component is destroyed |
navigated | NavigatedEventArgs | Fires when calendar view is navigated |
renderDayCell | RenderDayCellEventArgs | Fires when each day cell is rendered |
Documentation & Navigation Guide
When the user needs help with DatePicker, guide them to the appropriate reference:
Getting Started
📄 Read: references/getting-started.md
- Installation via npm (@syncfusion/ej2-react-calendars)
- CSS theme imports (material3, bootstrap, fluent, tailwind)
- Component imports and setup
- Basic JSX implementation with DatePickerComponent
- Functional vs class component examples
- Running your first application
Date Formats & Input
📄 Read: references/date-formats-and-input.md
- Display format property and patterns (yyyy-MM-dd, dd/MM/yyyy, etc.)
- Custom format specifiers (# and 0 patterns)
- Input formats for flexible date entry (accepting multiple formats)
- Format examples with real-world scenarios
- Parsing and converting user input automatically
- Culture-based default formatting
Date Range & Validation
📄 Read: references/date-range-and-validation.md
- Min and max date properties for range restriction
- Range validation and error states
- strictMode for automatic out-of-range correction
- Out-of-range behavior and error handling
- Disabling dates outside valid range
- Edge cases and gotchas
Date Views & Navigation
📄 Read: references/date-views-and-navigation.md
- Start property (month, year, decade initial view)
- Depth property for restricting view levels
- Calendar navigation and user interactions
- Month and year selection shortcuts
- Navigating between different views
- Default behavior and best practices
Customization & Styling
📄 Read: references/customization-and-styling.md
- CSS classes for styling (e-datepicker, e-calendar, e-day, etc.)
- renderDayCell event for day customization
- Disabling specific dates and weekends
- Placeholder, disabled, and readonly states
- Custom CSS and theme customization
- Day cell appearance and behavior
Globalization & Localization
📄 Read: references/globalization-and-localization.md
- Culture and locale configuration (German, French, Arabic, etc.)
- Loading CLDR data for internationalization
- Date format by culture (different countries, different formats)
- Locale text customization (today button, placeholder)
- Right-to-Left (RTL) support for Arabic, Hebrew, Urdu
- Week start day by culture
- Number formatting and calendar adjustments
Accessibility & Keyboard Navigation
📄 Read: references/accessibility-and-keyboard.md
- WCAG 2.2 compliance and accessibility standards
- Keyboard navigation shortcuts (Alt+Down, arrow keys, Esc)
- ARIA attributes (aria-expanded, aria-disabled, aria-activedescendant)
- Screen reader support and announcements
- Focus management and visible focus indicators
- Color contrast and visual accessibility
- Mobile device support
Date Masking & Advanced Validation
📄 Read: references/date-masking-and-strict-mode.md
enableMaskproperty for structured segment-by-segment date inputmaskPlaceholderfor custom segment placeholder text- Date masking patterns for input guidance
strictModeproperty behavior and enforcement- Date parsing rules and validation logic
- Input validation and format enforcement
- Edge cases (leap years, month boundaries, etc.)
- Troubleshooting common validation issues
- Best practices for date input
Quick Start Example
Here's a minimal working example to get started:
import React, { useState } from 'react';
import { DatePickerComponent } from '@syncfusion/ej2-react-calendars';
import '@syncfusion/ej2-base/styles/material3.css';
import '@syncfusion/ej2-buttons/styles/material3.css';
import '@syncfusion/ej2-inputs/styles/material3.css';
import '@syncfusion/ej2-popups/styles/material3.css';
import '@syncfusion/ej2-react-calendars/styles/material3.css';
export default function App() {
const [selectedDate, setSelectedDate] = useState(new Date());
return (
<div style={{ padding: '20px' }}>
<h3>Select a Date</h3>
<DatePickerComponent
value={selectedDate}
change={(e) => setSelectedDate(e.value)}
placeholder="Enter date"
/>
<p>Selected: {selectedDate?.toDateString()}</p>
</div>
);
}Key points:
- Import
DatePickerComponentfrom@syncfusion/ej2-react-calendars - Import all required CSS themes (base, buttons, inputs, popups, calendars)
- Use
valueprop for the current date (can be null or Date object) - Use
changeevent (notonChange) to update React state — this is the Syncfusion event name - DatePicker opens a calendar popup on click or Alt+Down arrow
Common Patterns
1. Date Range Picker (Min/Max Dates)
<DatePickerComponent
value={new Date()}
min={new Date(2026, 0, 1)}
max={new Date(2026, 11, 31)}
placeholder="Select a date in 2026"
/>2. Custom Date Format
<DatePickerComponent
value={new Date()}
format="dd/MM/yyyy"
placeholder="DD/MM/YYYY"
/>3. Multiple Accepted Input Formats
<DatePickerComponent
value={new Date()}
format="yyyy-MM-dd"
inputFormats={['dd/MM/yyyy', 'yyyy-MM-dd', 'yyyyMMdd']}
placeholder="Enter date (dd/MM/yyyy or yyyy-MM-dd)"
/>4. Year/Decade View for Birth Date Selection
<DatePickerComponent
value={new Date()}
start="Decade"
depth="Year"
placeholder="Select year"
/>5. Disable Weekends
<DatePickerComponent
value={new Date()}
renderDayCell={(args) => {
if ((args.date.getDay()) === 0 || (args.date.getDay()) === 6) {
args.isDisabled = true;
}
}}
placeholder="Weekdays only"
/>6. Controlled Component in React Form
const [date, setDate] = useState(null);
<DatePickerComponent
value={date}
change={(e) => setDate(e.value)}
format="yyyy-MM-dd"
strictMode={true}
placeholder="Enter date"
/>7. German Culture with RTL Support
<DatePickerComponent
locale="de"
enableRtl={false}
firstDayOfWeek={1}
value={new Date()}
placeholder="Datum eingeben"
/>8. Masked Date Input
<DatePickerComponent
enableMask={true}
format="MM/dd/yyyy"
maskPlaceholder={{ day: 'DD', month: 'MM', year: 'YYYY' }}
placeholder="Select a date"
/>9. Programmatic Control
import { useRef } from 'react';
const datePickerRef = useRef(null);
// Open calendar
datePickerRef.current.show();
// Close calendar
datePickerRef.current.hide();
// Focus
datePickerRef.current.focusIn();
// Get current view
const view = datePickerRef.current.currentView(); // "Month" | "Year" | "Decade"
// Navigate to specific view
datePickerRef.current.navigateTo('Year', new Date(2026, 0, 1));
<DatePickerComponent ref={datePickerRef} value={new Date()} />10. Handle All Key Events
<DatePickerComponent
value={new Date()}
change={(e) => console.log('Changed:', e.value)}
focus={(e) => console.log('Focused')}
blur={(e) => console.log('Blurred')}
open={(e) => console.log('Opened')}
close={(e) => console.log('Closed')}
cleared={(e) => console.log('Cleared')}
navigated={(e) => console.log('Navigated to view:', e.view)}
renderDayCell={(args) => {
// Disable weekends
if (args.date.getDay() === 0 || args.date.getDay() === 6) {
args.isDisabled = true;
}
}}
/>DateRangePicker
The Syncfusion React DateRangePickerComponent enables users to select a start and end date range with built-in support for presets, validation, custom formatting, separator configuration, full-screen mobile mode, and advanced range constraints (minDays, maxDays, min, max).
Documentation Navigation Guide
Getting Started
📄 Read: references/getting-started.md
- Installation via npm (@syncfusion/ej2-react-calendars)
- CSS imports and theme configuration
- Basic DateRangePicker implementation
- Class component vs functional component setup
- Component initialization and structure
- Running development server
- Common troubleshooting
Date Range Selection
📄 Read: references/date-range-selection.md
- Start and end date properties
- Date range validation patterns
- Minimum and maximum date constraints
- Disabled dates configuration
- Date range presets (Last 7 days, Last 30 days, etc.)
- Value binding and two-way updates
- Read-only and disabled states
- Placeholder and labels
Date Range Formatting
📄 Read: references/date-range-formatting.md
- Date format string options (MM/dd/yyyy, dd-MMM-yyyy, etc.)
- Display format vs input format
- Locale-based date formatting
- Custom separator between start and end dates
- Float label types (Never, Always, Auto)
- Placeholder text customization
- htmlAttributes for DOM attributes
Events and Methods
📄 Read: references/events-and-methods.md
- Event handlers (change, open, close, blur, focus, select)
- Event argument structures
- Methods (show, hide, focusIn, focusOut, reset, destroy)
- Imperative control with useRef
- Lifecycle events (created, destroyed)
- Event patterns and best practices
- Clearing values and state reset
- DateRangeSelectingEvent and ChangedEventArgs
Customization and Styling
📄 Read: references/customization-and-styling.md
- CSS class customization with cssClass
- Theme options (Material, Bootstrap, Fluent, Tailwind, Fabric)
- Full-screen mode for mobile devices
- RTL (right-to-left) language support
- Preset ranges customization
- Z-index management
- Width and height configuration
- Accessibility features and ARIA attributes
API Reference
📄 Read: references/api-reference.md
- Complete properties list (35+ properties)
- All methods with signatures (8 methods)
- All events with event arguments (12 events)
- Type definitions and interfaces
- Default values and constraints
- Use cases for each property and method
Advanced Patterns
📄 Read: references/advanced-patterns.md
- Form submission with date range validation
- Keyboard shortcuts and key navigation
- Server timezone offset handling
- Persistence and localStorage
- Multi-component integration (start/end date binding)
- Performance optimization with lazy loading
- Error handling and validation patterns
- Complex date range scenarios (fiscal years, quarters)
Quick Start
import { DateRangePickerComponent } from '@syncfusion/ej2-react-calendars';
import * as React from 'react';
import '@syncfusion/ej2-base/styles/material3.css';
import '@syncfusion/ej2-buttons/styles/material3.css';
import '@syncfusion/ej2-lists/styles/material3.css';
import '@syncfusion/ej2-inputs/styles/material3.css';
import '@syncfusion/ej2-popups/styles/material3.css';
import '@syncfusion/ej2-calendars/styles/material3.css';
function App() {
const [selectedRange, setSelectedRange] = React.useState<[Date, Date] | null>(null);
const handleDateRangeChange = (e: any) => {
setSelectedRange([e.startDate, e.endDate]);
};
return (
<div style={{ padding: '20px' }}>
<h2>Select Date Range</h2>
<DateRangePickerComponent
id="daterangepicker"
placeholder="Select a range"
change={handleDateRangeChange}
/>
{selectedRange && (
<p>
Selected: {selectedRange[0]?.toLocaleDateString()} - {selectedRange[1]?.toLocaleDateString()}
</p>
)}
</div>
);
}
export default App;Common Patterns
Pattern 1: Date Range with Preset Options
import { DateRangePickerComponent } from '@syncfusion/ej2-react-calendars';
import * as React from 'react';
function ReportingDashboard() {
const [dateRange, setDateRange] = React.useState<[Date, Date] | null>(null);
const getDateRangePresets = () => {
const today = new Date();
const yesterday = new Date(today);
yesterday.setDate(today.getDate() - 1);
const last7Days = new Date(today);
last7Days.setDate(today.getDate() - 7);
const last30Days = new Date(today);
last30Days.setDate(today.getDate() - 30);
const thisMonth = new Date(today.getFullYear(), today.getMonth(), 1);
const lastMonth = new Date(today.getFullYear(), today.getMonth(), 0);
return [
{ text: 'Today', value: [today, today] },
{ text: 'Yesterday', value: [yesterday, yesterday] },
{ text: 'Last 7 Days', value: [last7Days, today] },
{ text: 'Last 30 Days', value: [last30Days, today] },
{ text: 'This Month', value: [thisMonth, today] },
{ text: 'Last Month', value: [new Date(today.getFullYear(), today.getMonth() - 1, 1), lastMonth] },
];
};
const handlePresetClick = (start: Date, end: Date) => {
setDateRange([start, end]);
};
return (
<div style={{ padding: '20px' }}>
<h3>Analytics Report</h3>
<DateRangePickerComponent
id="daterangepicker"
placeholder="Select report date range"
startDate={dateRange?.[0]}
endDate={dateRange?.[1]}
change={(e: any) => setDateRange([e.startDate, e.endDate])}
/>
<div style={{ marginTop: '15px' }}>
{getDateRangePresets().map((preset) => (
<button
key={preset.text}
onClick={() => handlePresetClick(preset.value[0], preset.value[1])}
style={{ marginRight: '10px', padding: '5px 10px' }}
>
{preset.text}
</button>
))}
</div>
</div>
);
}
export default ReportingDashboard;Pattern 2: Date Range with Validation
import { DateRangePickerComponent } from '@syncfusion/ej2-react-calendars';
import { ButtonComponent } from '@syncfusion/ej2-react-buttons';
import * as React from 'react';
function BookingForm() {
const [dateRange, setDateRange] = React.useState<[Date, Date] | null>(null);
const [validationError, setValidationError] = React.useState<string>('');
const minDate = new Date();
const maxDate = new Date();
maxDate.setDate(maxDate.getDate() + 90); // 90 days from now
const handleDateRangeChange = (e: any) => {
setValidationError('');
if (!e.startDate || !e.endDate) {
return;
}
const start = new Date(e.startDate);
const end = new Date(e.endDate);
// Validation checks
if (start > end) {
setValidationError('Start date must be before end date');
return;
}
const daysDifference = (end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24);
if (daysDifference > 30) {
setValidationError('Date range cannot exceed 30 days');
return;
}
setDateRange([start, end]);
};
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (dateRange && !validationError) {
console.log('Booking dates:', {
checkIn: dateRange[0].toLocaleDateString(),
checkOut: dateRange[1].toLocaleDateString(),
});
}
};
return (
<form onSubmit={handleSubmit} style={{ padding: '20px', maxWidth: '500px' }}>
<h3>Book Your Stay</h3>
<label style={{ display: 'block', marginBottom: '8px' }}>
Select Check-in and Check-out Dates (Max 30 days)
</label>
<DateRangePickerComponent
id="daterangepicker"
placeholder="Select check-in and check-out"
min={minDate}
max={maxDate}
startDate={dateRange?.[0]}
endDate={dateRange?.[1]}
change={handleDateRangeChange}
style={{ width: '100%', marginBottom: '8px' }}
/>
{validationError && (
<div style={{ color: 'red', marginBottom: '10px', fontSize: '14px' }}>
⚠️ {validationError}
</div>
)}
<ButtonComponent
type="submit"
isPrimary={true}
disabled={!dateRange || !!validationError}
>
Book Now
</ButtonComponent>
</form>
);
}
export default BookingForm;<!-- Pattern 3 removed: examples using non-API props (e.g., disabledDates) deleted to match authoritative API reference -->
Pattern 4: Event Handling and State Management
import { DateRangePickerComponent } from '@syncfusion/ej2-react-calendars';
import * as React from 'react';
function EventTrackingExample() {
const [dateRange, setDateRange] = React.useState<[Date, Date] | null>(null);
const [eventLog, setEventLog] = React.useState<string[]>([]);
const handleSelect = (e: any) => {
setEventLog(prev => [
...prev,
`Selected: ${e.startDate?.toLocaleDateString()} to ${e.endDate?.toLocaleDateString()}`
]);
};
const handleChange = (e: any) => {
setDateRange([e.startDate, e.endDate]);
setEventLog(prev => [...prev, `Changed: ${new Date().toLocaleTimeString()}`]);
};
const handleOpen = (e: any) => {
setEventLog(prev => [...prev, `Popup opened at ${new Date().toLocaleTimeString()}`]);
};
const handleClose = (e: any) => {
setEventLog(prev => [...prev, `Popup closed at ${new Date().toLocaleTimeString()}`]);
};
const clearLog = () => {
setEventLog([]);
};
return (
<div style={{ padding: '20px', display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '20px' }}>
<div>
<h3>DateRangePicker</h3>
<DateRangePickerComponent
id="daterangepicker"
placeholder="Select date range"
select={handleSelect}
change={handleChange}
open={handleOpen}
close={handleClose}
/>
</div>
<div style={{ padding: '10px', border: '1px solid #ccc', borderRadius: '4px' }}>
<h3>Event Log</h3>
<button
onClick={clearLog}
style={{
padding: '5px 10px',
marginBottom: '10px',
backgroundColor: '#f0f0f0',
border: '1px solid #ccc',
cursor: 'pointer'
}}
>
Clear Log
</button>
<ul style={{ maxHeight: '300px', overflowY: 'auto', margin: 0, paddingLeft: '20px' }}>
{eventLog.map((event, idx) => (
<li key={idx} style={{ marginBottom: '5px', fontSize: '12px' }}>
{event}
</li>
))}
</ul>
</div>
</div>
);
}
export default EventTrackingExample;Pattern 5: Custom Date Range Format
import { DateRangePickerComponent } from '@syncfusion/ej2-react-calendars';
import * as React from 'react';
function DateFormatDemo() {
const [formatType, setFormatType] = React.useState<'short' | 'long' | 'custom'>('short');
const [dateRange, setDateRange] = React.useState<[Date, Date] | null>(null);
const getFormat = () => {
switch (formatType) {
case 'short':
return 'M/d/yyyy';
case 'long':
return 'MMMM d, yyyy';
case 'custom':
return 'dd-MMM-yy';
default:
return 'M/d/yyyy';
}
};
return (
<div style={{ padding: '20px' }}>
<h3>Date Range Format Options</h3>
<div style={{ marginBottom: '15px' }}>
<label style={{ marginRight: '10px' }}>Format Type:</label>
{(['short', 'long', 'custom'] as const).map((format) => (
<label key={format} style={{ marginRight: '15px' }}>
<input
type="radio"
name="format"
value={format}
checked={formatType === format}
onChange={(e) => setFormatType(e.target.value as any)}
/>
{format.charAt(0).toUpperCase() + format.slice(1)}
</label>
))}
</div>
<div style={{ marginBottom: '10px', padding: '10px', backgroundColor: '#f5f5f5' }}>
<strong>Format String:</strong> {getFormat()}
</div>
<DateRangePickerComponent
id="daterangepicker"
placeholder="Select date range"
format={getFormat()}
startDate={dateRange?.[0]}
endDate={dateRange?.[1]}
change={(e: any) => setDateRange([e.startDate, e.endDate])}
/>
{dateRange && (
<div style={{ marginTop: '15px', padding: '10px', backgroundColor: '#e8f5e9' }}>
<p>
<strong>Formatted Output:</strong> {dateRange[0].toLocaleDateString('en-US')} - {dateRange[1].toLocaleDateString('en-US')}
</p>
</div>
)}
</div>
);
}
export default DateFormatDemo;Key Props Reference
- Prop:
startDate: Type:Date— Default:null— Initial start date of the range. - Prop:
endDate: Type:Date— Default:null— Initial end date of the range. - Prop:
min: Type:Date— Default:new Date(1900, 0, 1)— Minimum selectable date. - Prop:
max: Type:Date— Default:new Date(2099, 11, 31)— Maximum selectable date. - Prop:
value: Type:Date[] | DateRange— Default:null— Gets or sets the start and end date. - Prop:
format: Type:string | RangeFormatObject— Default:null— Date display and input format. - Prop:
placeholder: Type:string— Default:null— Input placeholder text. - Prop:
enabled: Type:boolean— Default:true— Enables or disables the component (useenabled, notdisabled). - Prop:
readonly: Type:boolean— Default:false— Read-only state; prevents editing. - Prop:
allowEdit: Type:boolean— Default:true— Allow manual text editing of the input. - Prop:
cssClass: Type:string— Default:''— Adds a custom CSS class to the root element. - Prop:
floatLabelType: Type:FloatLabelType | string— Default:Never— Float label behavior (Never, Always, Auto). - Prop:
separator: Type:string— Default:'-'— Separator string between start and end date in the input. - Prop:
locale: Type:string— Default:'en-US'— Locale used for formatting and localization. - Prop:
inputFormats: Type:string[] | RangeFormatObject[]— Default:null— Acceptable input parsing formats. - Prop:
keyConfigs: Type:object— Default:null— Custom keyboard shortcuts mapping. - Prop:
firstDayOfWeek: Type:number— Default:null— First day of week for calendar rendering. - Prop:
dayHeaderFormat: Type:DayHeaderFormats— Default:Short— Day name format in header. - Prop:
start: Type:CalendarView— Default:Month— Initial calendar view when popup opens. - Prop:
depth: Type:CalendarView— Default:Month— Maximum navigation depth for the calendar. - Prop:
weekNumber: Type:boolean— Default:false— Show week numbers in calendar rows. - Prop:
weekRule: Type:WeekRule— Default:FirstDay— Rule that defines first week of the year. - Prop:
minDays: Type:number— Default:null— Minimum allowed span of days in a selection. - Prop:
maxDays: Type:number— Default:null— Maximum allowed span of days in a selection. - Prop:
strictMode: Type:boolean— Default:false— When true, only valid ranges can be entered. - Prop:
showClearButton: Type:boolean— Default:true— Toggle visibility of the clear button. - Prop:
fullScreenMode: Type:boolean— Default:false— Use full-screen popup on mobile. - Prop:
htmlAttributes: Type:{ [key: string]: string }— Default:{}— Additional HTML attributes applied to the component element. - Prop:
serverTimezoneOffset: Type:number— Default:null— Server timezone offset in minutes for initial value processing. - Prop:
width: Type:number | string— Default:''— Width of the component input. - Prop:
zIndex: Type:number— Default:1000— z-index for popup element.
---
Next Steps:
- Read references/getting-started.md to install and set up your first DateRangePicker
- Explore references/date-range-selection.md for range selection patterns
- Check references/date-range-formatting.md for format options
- See references/advanced-patterns.md for complex scenarios
- Refer to references/api-reference.md for complete API documentation
DateTimePicker
The Syncfusion React DateTimePickerComponent combines date and time selection in a single control. It offers calendar + time list popup, customizable time steps, masking, strict validation, timezone handling, format customization, and full keyboard accessibility.
Documentation (read these references in order)
- 📄 Read: references/getting-started.md — installation, module setup, CSS imports, basic usage
- 📄 Read: references/api-reference.md — full properties, methods, and events
- 📄 Read: references/date-time-selection.md — selection patterns and constraints
- 📄 Read: references/time-configuration.md — step, minTime/maxTime, scroll behavior
- 📄 Read: references/events-and-methods.md — event handlers and method usage
- 📄 Read: references/styling-and-customization.md — themes and cssClass usage
- 📄 Read: references/advanced-features.md — masked input, strict mode, calendar modes, timezone handling
- 📄 Read: references/accessibility.md — keyboard and ARIA guidance
Quick Start (React + TypeScript)
1. Install package:
npm install @syncfusion/ej2-react-calendars2. Import styles (in index.css or component CSS):
@import '../node_modules/@syncfusion/ej2-base/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-calendars/styles/material3.css';3. Minimal functional example (App.tsx):
import React, { useState } from 'react';
import { DateTimePickerComponent } from '@syncfusion/ej2-react-calendars';
export default function App() {
const [value, setValue] = useState<Date | null>(new Date());
return (
<div style={{ padding: 20 }}>
<h3>Choose date and time</h3>
<DateTimePickerComponent
value={value}
change={(e) => setValue((e as any).value)}
format="dd/MM/yyyy hh:mm a"
step={15}
placeholder="Select date and time"
/>
<p>Selected: {value ? value.toString() : 'none'}</p>
</div>
);
}Common Patterns
- Controlled value: bind
valueand update onchange. - Range enforcement: use
minandmaxfor dates,minTime/maxTimefor times. - Masked input: enable with
enableMaskand providemaskPlaceholder. - Localization: set
localeor use global culture settings. - Keyboard-first: provide
keyConfigsfor custom shortcuts.
Key Props Summary (see API reference for full list)
value,min,max,step,format,enableMask,placeholder,cssClass,locale,readonly,enabled.
Key Events
change,open,close,created,destroyed,navigated,blur,focus,renderDayCell.
Next steps
- All reference files have been created and validated against the official Syncfusion API (see
references/api-reference.md). - Next: run the test-case guide and validation checks, then create automated examples or add platform-specific notes on request.
- Ask me to run tests, update
completion-status.json, or produce publish-ready artifacts.
TimePicker
The Syncfusion React TimePickerComponent is a lightweight and feature-rich control for selecting time values. It supports 12/24-hour formats, time stepping, min/max constraints, masked input, localization, full-screen mode, and easy integration into React forms.
Documentation Navigation Guide
Getting Started
📄 Read: references/getting-started.md
- Installation via npm (@syncfusion/ej2-react-calendars)
- CalendarModule setup in app.module.ts
- CSS imports and theme configuration
- Basic TimePicker implementation
- Component registration with useRef
- Running development server
- Common troubleshooting
Time Format and Display
📄 Read: references/time-format-and-display.md
- Format string options (24-hour, 12-hour formats)
- TimeFormatObject with skeleton property
- Locale-based time formatting
- Placeholder text customization
- Float label types (Never, Always, Auto)
- htmlAttributes for DOM attributes
- Masked input with enableMask
- Mask placeholder configuration
Time Range and Selection
📄 Read: references/time-range-and-selection.md
- Minimum and maximum time constraints
- Time step intervals (15, 30, 60 minutes)
- ScrollTo default position
- Value binding and two-way updates
- Read-only and disabled states
- OpenOnFocus behavior
- Time popup list population
- Stepped time intervals
Events and Methods
📄 Read: references/events-and-methods.md
- Event handlers (change, open, close, blur, focus)
- Event argument structures
- Methods (show, hide, focusIn, focusOut)
- Imperative control with useRef
- Lifecycle events (created, destroyed)
- Event patterns and best practices
- Clearing values and state reset
- ItemRender for custom formatting
Customization and Styling
📄 Read: references/customization-and-styling.md
- CSS class customization with cssClass
- Theme options (Material, Bootstrap, Fluent, Tailwind)
- Full-screen mode for mobile devices
- RTL (right-to-left) language support
- Strict mode validation
- Z-index management
- Width and height configuration
- Accessibility features
- Theme Studio integration
API Reference
📄 Read: references/api-reference.md
- Complete properties list (26 properties)
- All methods with signatures (5 methods)
- All events with event arguments (9 events)
- Type definitions and interfaces
- Default values and constraints
- Use cases for each property
Advanced Patterns
📄 Read: references/advanced-patterns.md
- Form submission with validation
- Keyboard shortcuts and keyConfigs
- Server timezone offset handling
- Persistence and localStorage
- Multi-component integration
- Performance optimization
- Error handling patterns
- Complex validation scenarios
Quick Start
import { TimePickerComponent } from '@syncfusion/ej2-react-calendars';
import * as React from 'react';
import '@syncfusion/ej2-base/styles/material3.css';
import '@syncfusion/ej2-calendars/styles/material3.css';
function App() {
const [selectedTime, setSelectedTime] = React.useState(new Date('1/1/2018 9:00 AM'));
const handleChange = (e: any) => {
setSelectedTime(e.value);
};
return (
<div style={{ padding: '20px' }}>
<h2>Select Time</h2>
<TimePickerComponent
value={selectedTime}
change={handleChange}
placeholder="Select a time"
/>
<p>Selected: {selectedTime ? selectedTime.toLocaleTimeString() : 'None'}</p>
</div>
);
}
export default App;Common Patterns
Pattern 1: Time Picker with Min/Max Constraints
import { TimePickerComponent } from '@syncfusion/ej2-react-calendars';
import * as React from 'react';
function AppointmentScheduler() {
const [appointmentTime, setAppointmentTime] = React.useState(new Date('1/1/2018 9:00 AM'));
const minTime = new Date('1/1/2018 8:00 AM');
const maxTime = new Date('1/1/2018 5:00 PM');
return (
<div>
<h3>Select Appointment Time (8 AM - 5 PM)</h3>
<TimePickerComponent
value={appointmentTime}
min={minTime}
max={maxTime}
step={30}
change={(e: any) => setAppointmentTime(e.value)}
placeholder="Choose time"
/>
</div>
);
}
export default AppointmentScheduler;Pattern 2: Form with Time Picker Submission
import { TimePickerComponent } from '@syncfusion/ej2-react-calendars';
import { ButtonComponent } from '@syncfusion/ej2-react-buttons';
import * as React from 'react';
function ScheduleForm() {
const [formData, setFormData] = React.useState({
startTime: new Date('1/1/2018 9:00 AM'),
endTime: new Date('1/1/2018 5:00 PM'),
});
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
console.log('Schedule data:', {
startTime: formData.startTime?.toLocaleTimeString(),
endTime: formData.endTime?.toLocaleTimeString(),
});
};
return (
<form onSubmit={handleSubmit}>
<h3>Schedule Meeting</h3>
<label>Start Time:</label>
<TimePickerComponent
value={formData.startTime}
change={(e: any) => setFormData(prev => ({ ...prev, startTime: e.value }))}
/>
<label style={{ marginTop: '10px' }}>End Time:</label>
<TimePickerComponent
value={formData.endTime}
min={formData.startTime}
change={(e: any) => setFormData(prev => ({ ...prev, endTime: e.value }))}
/>
<ButtonComponent type="submit" isPrimary={true} style={{ marginTop: '15px' }}>
Schedule
</ButtonComponent>
</form>
);
}
export default ScheduleForm;Pattern 3: Time Picker with Custom Format
import { TimePickerComponent } from '@syncfusion/ej2-react-calendars';
import * as React from 'react';
function TimeFormatDemo() {
const [time12hr, setTime12hr] = React.useState(new Date('1/1/2018 2:30 PM'));
const [time24hr, setTime24hr] = React.useState(new Date('1/1/2018 14:30'));
return (
<div style={{ padding: '20px' }}>
<div>
<h4>12-Hour Format (hh:mm a)</h4>
<TimePickerComponent
value={time12hr}
format="hh:mm a"
change={(e: any) => setTime12hr(e.value)}
/>
<p>Value: {time12hr?.toLocaleTimeString('en-US', { hour12: true })}</p>
</div>
<div style={{ marginTop: '20px' }}>
<h4>24-Hour Format (HH:mm)</h4>
<TimePickerComponent
value={time24hr}
format="HH:mm"
change={(e: any) => setTime24hr(e.value)}
/>
<p>Value: {time24hr?.toLocaleTimeString('en-US', { hour12: false })}</p>
</div>
</div>
);
}
export default TimeFormatDemo;Pattern 4: Event Handling and State Management
import { TimePickerComponent } from '@syncfusion/ej2-react-calendars';
import * as React from 'react';
function EventTrackingExample() {
const [selectedTime, setSelectedTime] = React.useState<Date | null>(null);
const [eventLog, setEventLog] = React.useState<string[]>([]);
const handleChange = (e: any) => {
setSelectedTime(e.value);
setEventLog(prev => [...prev, `Changed: ${e.value?.toLocaleTimeString()}`]);
};
const handleOpen = (e: any) => {
setEventLog(prev => [...prev, 'Popup opened']);
};
const handleClose = (e: any) => {
setEventLog(prev => [...prev, 'Popup closed']);
};
return (
<div style={{ padding: '20px' }}>
<h3>Time Picker with Event Tracking</h3>
<TimePickerComponent
value={selectedTime}
change={handleChange}
open={handleOpen}
close={handleClose}
placeholder="Select time to track events"
/>
<div style={{ marginTop: '20px', padding: '10px', border: '1px solid #ccc' }}>
<h4>Event Log:</h4>
<ul>
{eventLog.map((event, idx) => (
<li key={idx}>{event}</li>
))}
</ul>
</div>
</div>
);
}
export default EventTrackingExample;Pattern 5: Masked Time Input
import { TimePickerComponent } from '@syncfusion/ej2-react-calendars';
import * as React from 'react';
function MaskedTimePickerExample() {
const [maskedTime, setMaskedTime] = React.useState(new Date('1/1/2018 10:30 AM'));
return (
<div style={{ padding: '20px' }}>
<h3>Masked Time Input</h3>
<TimePickerComponent
value={maskedTime}
enableMask={true}
format="hh:mm a"
maskPlaceholder={{
hour: 'HH',
minute: 'MM',
second: 'SS',
}}
change={(e: any) => setMaskedTime(e.value)}
placeholder="Enter time (HH:MM AM/PM)"
/>
<p>Masked input helps users enter time in correct format</p>
</div>
);
}
export default MaskedTimePickerExample;Key Props Reference
| Prop | Type | Default | Purpose |
|---|---|---|---|
value | Date | null | Current selected time value |
format | string | Based on culture | Time display format (e.g., "HH:mm", "hh:mm a") |
min | Date | 00:00 | Minimum selectable time |
max | Date | 00:00 | Maximum selectable time |
step | number | 30 | Time interval in minutes between list items |
enabled | boolean | true | Enable/disable the component |
readonly | boolean | false | Read-only state (no editing) |
placeholder | string | - | Input placeholder text |
openOnFocus | boolean | false | Open popup on input focus |
enableMask | boolean | false | Enable masked input mode |
enableRtl | boolean | false | Enable right-to-left layout |
strictMode | boolean | false | Validate input and restrict to valid times |
showClearButton | boolean | true | Show/hide clear button |
fullScreenMode | boolean | false | Mobile full-screen mode |
cssClass | string | - | Custom CSS class for styling |
floatLabelType | string | Never | Float label position |
allowEdit | boolean | true | Allow manual input editing |
locale | string | 'en-US' | Locale for time formatting |
scrollTo | Date | - | Default scroll position in popup |
width | string/number | - | Component width |
zIndex | number | 1000 | Z-index of popup |
serverTimezoneOffset | number | - | Server timezone offset for processing |
htmlAttributes | object | {} | Custom HTML attributes |
---
Next Steps:
- Read references/getting-started.md to install and set up your first TimePicker
- Explore references/time-format-and-display.md for format options
- Check references/time-range-and-selection.md for time constraints
- See references/advanced-patterns.md for complex scenarios
Accessibility & Globalization
Table of Contents
- Accessibility Overview
- WCAG Compliance
- Keyboard Navigation
- ARIA Attributes
- Screen Reader Support
- Globalization (Locales)
- RTL (Right-to-Left) Languages
- Testing for Accessibility
---
Accessibility Overview
The Syncfusion Calendar component is built with accessibility in mind, but you should verify and enhance it for your specific use case:
- Built-in ARIA labels and roles
- Keyboard navigation (Tab, Arrow keys, Enter)
- Focus indicators and state management
- Locale support for multiple languages
- RTL rendering for right-to-left languages
---
WCAG Compliance
The Calendar aims to comply with WCAG 2.1 Level AA standards:
- Perceivable: Color contrast, text alternatives, distinguishable elements
- Operable: Keyboard accessible, sufficient time, seizure prevention
- Understandable: Readable text, predictable behavior, clear labels
- Robust: Valid HTML, compatible with assistive technologies
Verification Checklist
- [ ] Text color contrast ≥ 4.5:1 for normal text, ≥ 3:1 for large text
- [ ] All interactive elements are keyboard accessible
- [ ] Focus indicators are visible (≥ 3px border or outline)
- [ ] Error messages are descriptive and announced to screen readers
- [ ] Date format is clear and consistent
---
Keyboard Navigation
The Calendar supports standard keyboard shortcuts:
| Key | Action |
|---|---|
Tab | Move focus to/from the calendar |
Arrow Up | Select date in previous week |
Arrow Down | Select date in next week |
Arrow Left | Select previous day |
Arrow Right | Select next day |
Enter / Space | Confirm selection |
Page Up | Navigate to previous month |
Page Down | Navigate to next month |
Ctrl + Page Up | Navigate to previous year |
Ctrl + Page Down | Navigate to next year |
Testing Keyboard Navigation
import React, { useState } from 'react';
import { CalendarComponent } from '@syncfusion/ej2-react-calendars';
export default function KeyboardA11yExample() {
const [value, setValue] = useState(new Date());
const onChange = (args) => {
console.log('Date selected via keyboard or mouse:', args.value);
};
return (
<div style={{ padding: 20 }}>
<label htmlFor="calendar-input" style={{ display: 'block', marginBottom: 10 }}>
Select a date (use keyboard arrows to navigate):
</label>
<CalendarComponent
id="calendar-input"
value={value}
change={onChange}
/>
<p>Selected: {value.toDateString()}</p>
</div>
);
}---
ARIA Attributes
The Calendar includes default ARIA attributes, but you should enhance them for context:
import React, { useState } from 'react';
import { CalendarComponent } from '@syncfusion/ej2-react-calendars';
export default function ARIAExample() {
const [value, setValue] = useState(new Date());
return (
<div style={{ padding: 20 }}>
{/* Container with ARIA labels */}
<div
role="region"
aria-labelledby="calendar-title"
aria-describedby="calendar-desc"
>
<h2 id="calendar-title">Event Date Picker</h2>
<p id="calendar-desc">
Use arrow keys to navigate dates. Press Enter to select.
</p>
{/* Wrap the calendar in a labelled container for accessibility context */}
<CalendarComponent
value={value}
change={(e) => setValue(e.value)}
/>
</div>
</div>
);
}Common ARIA Attributes (applied to the wrapper container)
aria-label— Provides an accessible name for the container regionaria-describedby— Links to a description element for the containerrole="region"— Marks the container as a significant sectionaria-live="polite"— On a separate live region element to announce date changes
Note:aria-label,tabIndex, and similar ARIA attributes are not official props ofCalendarComponent. Apply them to a wrapping<div>or use a live region element alongside the component, as shown in the examples.
---
Screen Reader Support
Testing with NVDA (Windows) or JAWS
1. Enable screen reader: Start NVDA (Ctrl+Alt+N) or JAWS. 2. Navigate the calendar: Use Tab to focus, arrow keys to select dates. 3. Listen for announcements: Screen reader should announce:
- Component role ("calendar")
- Current date being selected
- Month/year navigation changes
- Disabled date status
Announcing Date Changes
Enhance the change event with live region updates:
import React, { useState, useRef } from 'react';
import { CalendarComponent } from '@syncfusion/ej2-react-calendars';
export default function ScreenReaderExample() {
const [value, setValue] = useState(new Date());
const announceRef = useRef(null);
const onChange = (args) => {
setValue(args.value);
// Announce the selected date to screen readers
if (announceRef.current) {
announceRef.current.textContent = `Date selected: ${args.value.toDateString()}`;
}
};
return (
<div style={{ padding: 20 }}>
<CalendarComponent value={value} change={onChange} />
{/* Live region for screen reader announcements */}
<div
ref={announceRef}
aria-live="polite"
aria-atomic="true"
style={{ position: 'absolute', left: '-10000px' }}
/>
</div>
);
}---
Globalization (Locales)
The Calendar supports 100+ locales. Set the locale via the locale prop:
Common Locales
<CalendarComponent locale="en-US" /> // English (US)
<CalendarComponent locale="en-GB" /> // English (UK)
<CalendarComponent locale="de-DE" /> // German
<CalendarComponent locale="fr-FR" /> // French
<CalendarComponent locale="es-ES" /> // Spanish
<CalendarComponent locale="it-IT" /> // Italian
<CalendarComponent locale="ja-JP" /> // Japanese
<CalendarComponent locale="zh-CN" /> // Chinese (Simplified)
<CalendarComponent locale="hi-IN" /> // HindiExample: Multi-Language Picker
import React, { useState } from 'react';
import { CalendarComponent } from '@syncfusion/ej2-react-calendars';
export default function LocalePickerExample() {
const [locale, setLocale] = useState('en-US');
const [value, setValue] = useState(new Date());
const locales = [
{ code: 'en-US', name: 'English (US)' },
{ code: 'de-DE', name: 'Deutsch' },
{ code: 'fr-FR', name: 'Français' },
{ code: 'es-ES', name: 'Español' },
{ code: 'ja-JP', name: '日本語' },
];
return (
<div style={{ padding: 20 }}>
<label htmlFor="locale-select">Select Language:</label>
<select
id="locale-select"
value={locale}
onChange={(e) => setLocale(e.target.value)}
>
{locales.map((l) => (
<option key={l.code} value={l.code}>
{l.name}
</option>
))}
</select>
<div style={{ marginTop: 20 }}>
<CalendarComponent
locale={locale}
value={value}
change={(e) => setValue(e.value)}
/>
<p>Selected: {value.toLocaleDateString(locale)}</p>
</div>
</div>
);
}Date Formatting by Locale
const date = new Date(2026, 2, 15); // March 15, 2026
console.log(date.toLocaleDateString('en-US')); // 3/15/2026
console.log(date.toLocaleDateString('de-DE')); // 15.3.2026
console.log(date.toLocaleDateString('fr-FR')); // 15/03/2026
console.log(date.toLocaleDateString('ja-JP')); // 2026/3/15---
RTL (Right-to-Left) Languages
For Arabic, Hebrew, Persian, and other RTL languages, set both the locale and RTL flag:
import React, { useState } from 'react';
import { CalendarComponent } from '@syncfusion/ej2-react-calendars';
export default function RTLExample() {
const [value, setValue] = useState(new Date());
return (
<div style={{ padding: 20, direction: 'rtl', textAlign: 'right' }}>
<h3>تقويم يومي</h3>
<CalendarComponent
locale="ar"
enableRtl={true}
value={value}
change={(e) => setValue(e.value)}
/>
<p>التاريخ المختار: {value.toLocaleDateString('ar-EG')}</p>
</div>
);
}CSS for RTL Containers
/* Global RTL support */
[dir='rtl'] {
direction: rtl;
text-align: right;
}
/* Calendar-specific RTL */
.e-calendar[data-rtl="true"] {
flex-direction: row-reverse;
}RTL Testing Checklist
- [ ] Calendar layout is mirrored (left/right flipped)
- [ ] Day-of-week headers are in correct RTL order
- [ ] Navigation buttons (prev/next) are in correct positions
- [ ] Text and numbers are right-aligned
- [ ] Week start day matches locale (Sunday vs. Monday vs. Saturday)
---
Testing for Accessibility
Automated Tools
1. axe DevTools — Chrome extension for instant accessibility audits
- Right-click → Inspect → axe DevTools → Scan
2. Lighthouse — Built into Chrome DevTools
- DevTools → Lighthouse → Audit for accessibility
3. WAVE — WebAIM's web accessibility tool
- Browser extension or online validator
Manual Testing
1. Keyboard-only navigation: Unplug mouse and navigate the calendar using Tab, arrows, Enter. 2. Screen reader testing: Use NVDA (Windows) or VoiceOver (Mac) to verify announcements. 3. Color contrast: Use Color Contrast Analyzer to verify sufficient ratios. 4. Focus indicators: Tab through; ensure focus outline is visible at all times.
Example Test Checklist
// A11y Test Suite (pseudo-code)
describe('Calendar Accessibility', () => {
it('should be keyboard navigable', () => {
// Simulate Tab, Arrow keys, Enter
// Verify date changes and focus moves
});
it('should announce changes to screen readers', () => {
// Enable screen reader
// Change date
// Verify live region announces
});
it('should have sufficient color contrast', () => {
// Check computed style of selected date
// Verify contrast ≥ 4.5:1
});
it('should have visible focus indicators', () => {
// Tab to calendar
// Verify outline or border is visible
});
it('should support RTL locales', () => {
// Set locale to 'ar', enableRtl={true}
// Verify layout is mirrored
});
});---
Best Practices
1. Always provide labels: Use <label> elements or aria-label for context. 2. Test with actual assistive technologies: Don't rely only on browser DevTools. 3. Provide alternative date input: Offer a text input alongside the calendar for users who prefer typing. 4. Ensure sufficient spacing: Touch targets should be at least 44x44 pixels on mobile. 5. Test with real users: Especially those who rely on assistive technologies. 6. Document locale and RTL support: Inform users which languages and scripts are supported.
---
Resources
API Reference
Source: Syncfusion React Calendar API documentation — https://ej2.syncfusion.com/react/documentation/api/calendar/index-default
Table of Contents
---
Properties
| Property | Type | Default | Description |
|---|---|---|---|
calendarMode | CalendarType | 'Gregorian' | Gets or sets the Calendar's type — 'Gregorian' or 'Islamic'. |
cssClass | string | null | Root CSS class for the Calendar, used to override styles. |
dayHeaderFormat | DayHeaderFormats | 'Short' | Format of day names in the header. Options: 'Short', 'Narrow', 'Abbreviated', 'Wide'. |
depth | CalendarView | 'Month' | Maximum navigation level. Must be ≤ start. Options: 'Month', 'Year', 'Decade'. |
enablePersistence | boolean | false | Persists the value state across page reloads. |
enableRtl | boolean | false | Renders the component in right-to-left direction. |
enabled | boolean | true | Enables or disables the component. |
firstDayOfWeek | number | 0 | First day of the week (0 = Sunday). Defaults to current culture. |
isMultiSelection | boolean | false | Enables multiple date selection. Use with the values prop. |
keyConfigs | { [key: string]: string } | null | Customizes keyboard shortcut mappings. |
locale | string | '' | Overrides global culture/localization (e.g., 'en-US', 'fr-FR'). |
max | Date | new Date(2099, 11, 31) | Maximum selectable date. |
min | Date | new Date(1900, 00, 01) | Minimum selectable date. |
serverTimezoneOffset | number | null | Processes the initial date using a server time zone offset. |
showTodayButton | boolean | true | Shows or hides the Today button. |
start | CalendarView | 'Month' | Initial view when the Calendar opens. Options: 'Month', 'Year', 'Decade'. |
value | Date | null | The selected date. |
values | Date[] | null | Multiple selected dates. Used with isMultiSelection={true}. |
weekNumber | boolean | false | Shows the ISO week number of the year. |
weekRule | WeekRule | 'FirstDay' | Rule for defining the first week of the year. |
Tip: For controlled usage in React, passvalueand update it inchangeevent handlers.
keyConfigs Example
import React from 'react';
import { CalendarComponent } from '@syncfusion/ej2-react-calendars';
export default function KeyConfigsExample() {
const keyConfigs = {
select: 'space',
home: 'ctrl+home',
end: 'ctrl+end',
};
return <CalendarComponent keyConfigs={keyConfigs} />;
}isMultiSelection + values Example
import React from 'react';
import { CalendarComponent } from '@syncfusion/ej2-react-calendars';
export default function MultiSelectExample() {
const values = [new Date('11/20/2026'), new Date('11/28/2026'), new Date('11/02/2026')];
return <CalendarComponent isMultiSelection={true} values={values} />;
}---
Methods
Call these methods via a ref to the CalendarComponent instance.
| Method | Signature | Returns | Description |
|---|---|---|---|
addDate | `addDate(dates: Date \ | Date[]): void` | void |
currentView | currentView(): string | string | Returns the current view — 'Month', 'Year', or 'Decade'. |
destroy | destroy(): void | void | Destroys the widget and cleans up resources. |
getPersistData | getPersistData(): string | string | Returns the properties maintained on browser refresh. |
navigateTo | navigateTo(view: CalendarView, date: Date, isCustomDate?: boolean): void | void | Navigates to the specified view and focused date. |
removeDate | `removeDate(dates: Date \ | Date[]): void` | void |
navigateTo Example
import React, { useRef } from 'react';
import { CalendarComponent } from '@syncfusion/ej2-react-calendars';
export default function NavigateToExample() {
const calendarRef = useRef(null);
const goToJuly2026 = () => {
if (calendarRef.current) {
// Signature: navigateTo(view, date, isCustomDate?)
calendarRef.current.navigateTo('Month', new Date(2026, 6, 1));
}
};
return (
<div>
<CalendarComponent ref={calendarRef} />
<button onClick={goToJuly2026}>Go to July 2026</button>
</div>
);
}addDate / removeDate Example
import React, { useRef } from 'react';
import { CalendarComponent } from '@syncfusion/ej2-react-calendars';
export default function AddRemoveDateExample() {
const calendarRef = useRef(null);
const addDates = () => {
calendarRef.current?.addDate([new Date(2026, 10, 5), new Date(2026, 10, 10)]);
};
const removeDates = () => {
calendarRef.current?.removeDate(new Date(2026, 10, 5));
};
return (
<div>
<CalendarComponent ref={calendarRef} isMultiSelection={true} />
<button onClick={addDates}>Add Dates</button>
<button onClick={removeDates}>Remove Date</button>
</div>
);
}currentView Example
const getView = () => {
if (calendarRef.current) {
console.log('Current view:', calendarRef.current.currentView());
// Returns: 'Month', 'Year', or 'Decade'
}
};---
Events
| Event | Type | Description |
|---|---|---|
change | EmitType<ChangedEventArgs> | Fires when the Calendar value is changed. |
created | EmitType<Object> | Fires when the Calendar is created. |
destroyed | EmitType<Object> | Fires when the Calendar is destroyed. |
navigated | EmitType<NavigatedEventArgs> | Fires when the Calendar navigates to another level or within the same level. |
renderDayCell | EmitType<RenderDayCellEventArgs> | Fires when each day cell is rendered; use to customize cells. |
change Event Example
const onChange = (args) => {
// args.value — the selected Date
console.log('Selected:', args.value);
};
<CalendarComponent change={onChange} />navigated Event Example
const onNavigated = (args) => {
console.log('Navigated:', args);
};
<CalendarComponent navigated={onNavigated} />renderDayCell Event Example
const renderDayCell = (args) => {
// args.date — Date for this cell
// args.cellElement — the DOM element
// args.isDisabled — set true to disable
const day = args.date.getDay();
if (day === 0 || day === 6) {
args.isDisabled = true;
}
};
<CalendarComponent renderDayCell={renderDayCell} />---
Enums
CalendarView
Used for start, depth props, and the navigateTo method.
| Value | Description |
|---|---|
'Month' | Month grid view (default) |
'Year' | Yearly view showing all 12 months |
'Decade' | Decade view showing a 10-year range |
CalendarType
Used for the calendarMode prop.
| Value | Description |
|---|---|
'Gregorian' | Standard Gregorian calendar (default) |
'Islamic' | Islamic (Hijri) calendar |
DayHeaderFormats
Used for the dayHeaderFormat prop.
| Value | Example | Description |
|---|---|---|
'Short' | Su | Short format (default) |
'Narrow' | S | Single character |
'Abbreviated' | Sun | Abbreviated format |
'Wide' | Sunday | Full day name |
WeekRule
Used for the weekRule prop.
| Value | Description |
|---|---|
'FirstDay' | Week 1 starts on the first day of the year (default) |
'FirstFourDayWeek' | Week 1 is the week with ≥ 4 days in the new year |
'FirstFullWeek' | Week 1 is the first week fully in the new year |
---
Examples
Controlled Calendar with min/max
import React, { useState } from 'react';
import { CalendarComponent } from '@syncfusion/ej2-react-calendars';
export default function ControlledExample() {
const [value, setValue] = useState(new Date());
return (
<CalendarComponent
value={value}
min={new Date(2020, 0, 1)}
max={new Date(2030, 11, 31)}
change={(e) => setValue(e.value)}
/>
);
}Week Numbers + Today Button
<CalendarComponent weekNumber={true} showTodayButton={true} />Islamic Calendar Mode
<CalendarComponent calendarMode="Islamic" />Multi-Selection (controlled)
import React, { useState } from 'react';
import { CalendarComponent } from '@syncfusion/ej2-react-calendars';
export default function MultiSelectControlled() {
const [values, setValues] = useState([
new Date(2026, 10, 5),
new Date(2026, 10, 12),
]);
return (
<CalendarComponent
isMultiSelection={true}
values={values}
change={(args) => {
if (args.values) setValues(args.values);
}}
/>
);
}---
Official Documentation
- API Reference: https://ej2.syncfusion.com/react/documentation/api/calendar/index-default
- CalendarView enum: https://ej2.syncfusion.com/react/documentation/api/calendar/calendarview
- ChangedEventArgs: https://ej2.syncfusion.com/react/documentation/api/calendar/changedeventargs
- NavigatedEventArgs: https://ej2.syncfusion.com/react/documentation/api/calendar/navigatedeventargs
- RenderDayCellEventArgs: https://ej2.syncfusion.com/react/documentation/api/calendar/renderdaycelleventargs
Calendar Views and Navigation
Table of Contents
- View Types
- Navigating Between Views
- Controlling Initial View
- Limiting View Depth
- Month, Year, and Decade Selection
- Programmatic Navigation
---
View Types
The Calendar has three hierarchical views:
1. Month (default) — Shows a single month grid. User can click dates or navigate months. 2. Year — Shows all 12 months of a year. User can click a month or navigate years. 3. Decade — Shows a 10-year range (e.g., 2020–2029). User can click a year or navigate decades.
Decade View: 2020 2021 2022 ... 2029
↓ (click a year)
Year View: Jan Feb Mar ... Dec
↓ (click a month)
Month View: 1 2 3 ... 28 29 30 31
↓ (click a day)
Selection complete---
Navigating Between Views
Automatic (Click-based)
In Month view:
- Click month header (e.g., "March 2026") → navigate to Year view for that year.
- Click year header (e.g., "2026") → navigate to Decade view containing that year.
In Year view:
- Click month name → select that month and return to Month view.
In Decade view:
- Click year number → navigate to Year view for that year.
Programmatic (using ref)
The navigateTo method requires two arguments: the target view (CalendarView) and the focused date (Date).
import React, { useRef } from 'react';
import { CalendarComponent } from '@syncfusion/ej2-react-calendars';
export default function NavigationExample() {
const calendarRef = useRef(null);
const goToJuly2026 = () => {
if (calendarRef.current) {
// navigateTo(view: CalendarView, date: Date)
calendarRef.current.navigateTo('Month', new Date(2026, 6, 1));
}
};
const goToYear2025 = () => {
if (calendarRef.current) {
calendarRef.current.navigateTo('Year', new Date(2025, 0, 1));
}
};
return (
<div style={{ padding: 20 }}>
<CalendarComponent ref={calendarRef} />
<button onClick={goToJuly2026}>Go to July 2026</button>
<button onClick={goToYear2025}>Go to Year 2025</button>
</div>
);
}---
Controlling Initial View
Use the start prop to specify which view appears when the calendar loads.
import React from 'react';
import { CalendarComponent } from '@syncfusion/ej2-react-calendars';
export default function InitialViewExample() {
return (
<div style={{ padding: 20 }}>
<h3>Start in Month view (default)</h3>
<CalendarComponent start="Month" />
<h3>Start in Year view</h3>
<CalendarComponent start="Year" />
<h3>Start in Decade view</h3>
<CalendarComponent start="Decade" />
</div>
);
}Use case: If you want users to pick a year first, start with start="Year" or start="Decade".
---
Limiting View Depth
Use the depth prop to restrict the deepest view users can navigate to. Typically matches start.
import React from 'react';
import { CalendarComponent } from '@syncfusion/ej2-react-calendars';
export default function DepthExample() {
return (
<div style={{ padding: 20 }}>
<h3>Only Month view (no Year or Decade)</h3>
<CalendarComponent start="Month" depth="Month" />
<h3>Month and Year only (no Decade)</h3>
<CalendarComponent start="Month" depth="Year" />
<h3>All views enabled (default)</h3>
<CalendarComponent start="Month" depth="Decade" />
</div>
);
}Best practice: Set depth={start} for a cleaner UX, or use depth="Year" if you don't want users drilling down to individual decades.
---
Month, Year, and Decade Selection
Selecting a Month (in Month view)
const [selectedDate, setSelectedDate] = useState(new Date(2026, 2, 15)); // March 15, 2026
<CalendarComponent
value={selectedDate}
change={(e) => setSelectedDate(e.value)}
/>When the user clicks a day, the change event fires and args.value is the selected Date.
Selecting a Year (via Year view)
import React, { useRef, useState } from 'react';
import { CalendarComponent } from '@syncfusion/ej2-react-calendars';
export default function YearSelectionExample() {
const calendarRef = useRef(null);
const [selectedYear, setSelectedYear] = useState(2026);
const onChange = (args) => {
setSelectedYear(args.value.getFullYear());
console.log('Selected year:', args.value.getFullYear());
};
return (
<div style={{ padding: 20 }}>
<h3>Pick a year</h3>
<CalendarComponent
ref={calendarRef}
start="Year"
depth="Year"
change={onChange}
/>
<p>Selected year: {selectedYear}</p>
</div>
);
}Note: Even in Year view, the change event returns a full Date object. Extract the year with .getFullYear().
Selecting a Decade
import React, { useState } from 'react';
import { CalendarComponent } from '@syncfusion/ej2-react-calendars';
export default function DecadeSelectionExample() {
const [selectedYear, setSelectedYear] = useState(2025);
const onChange = (args) => {
setSelectedYear(args.value.getFullYear());
};
return (
<div style={{ padding: 20 }}>
<h3>Pick a year from the decade</h3>
<CalendarComponent
start="Decade"
depth="Decade"
change={onChange}
/>
<p>Selected year: {selectedYear}</p>
</div>
);
}---
Programmatic Navigation
Get Current View
const calendarRef = useRef(null);
const getCurrentView = () => {
if (calendarRef.current) {
const view = calendarRef.current.currentView();
console.log('Current view:', view); // 'Month', 'Year', or 'Decade'
}
};
<CalendarComponent ref={calendarRef} />
<button onClick={getCurrentView}>Check Current View</button>Navigate to Specific Date
The navigateTo method signature is: navigateTo(view: CalendarView, date: Date, isCustomDate?: boolean): void
const navigateTo = (view, year, month, day = 1) => {
if (calendarRef.current) {
calendarRef.current.navigateTo(view, new Date(year, month, day));
}
};
// Navigate to January 2025 in Month view
navigateTo('Month', 2025, 0, 1);
// Navigate to Year view for 2025
navigateTo('Year', 2025, 0, 1);Listen to Navigation (navigated event)
Some versions of Syncfusion support a navigated event that fires after view changes.
const onNavigated = (args) => {
console.log('Navigated. Current view:', args.value);
};
<CalendarComponent navigated={onNavigated} />---
Common Patterns
Year-Month Picker
Start in Year view, then move to Month view:
<CalendarComponent start="Year" depth="Month" />When user clicks a month, change fires and you get a Date (first day of the selected month by default).
Quick Year/Decade Selector
For forms where you only need year selection:
<CalendarComponent start="Year" depth="Year" />The user only sees years and cannot drill down to individual months/days.
Multi-month Navigation
Navigate to the next/previous month programmatically:
const goToPrevMonth = () => {
const ref = calendarRef.current;
if (ref && ref.value) {
const prev = new Date(ref.value);
prev.setMonth(prev.getMonth() - 1);
// navigateTo(view, date)
ref.navigateTo('Month', prev);
}
};
const goToNextMonth = () => {
const ref = calendarRef.current;
if (ref && ref.value) {
const next = new Date(ref.value);
next.setMonth(next.getMonth() + 1);
ref.navigateTo('Month', next);
}
};
<button onClick={goToPrevMonth}>← Previous</button>
<CalendarComponent ref={calendarRef} />
<button onClick={goToNextMonth}>Next →</button>---
Notes
- View transitions are automatic when you click headers or month/year cells; you typically don't need to change
startordepthafter initialization. - The `change` event always returns a `Date` object, regardless of the current view.
- Programmatic navigation via
navigateTo()is useful for keyboard shortcuts, external buttons, or conditional flows.
Date Selection Patterns
Table of Contents
- Single Date Selection
- Multiple Date Selection
- Date Range Selection
- Min/Max Date Constraints
- Disabling Specific Dates
- Reading Selection State
---
Single Date Selection
The default Calendar behavior. User clicks a date, and it becomes selected.
import React, { useState } from 'react';
import { CalendarComponent } from '@syncfusion/ej2-react-calendars';
export default function SingleDateExample() {
const [selectedDate, setSelectedDate] = useState(new Date());
const onChange = (args) => {
console.log('Selected date:', args.value);
setSelectedDate(args.value);
};
return (
<div style={{ padding: 20 }}>
<h3>Pick a single date</h3>
<CalendarComponent value={selectedDate} change={onChange} />
<p>You picked: {selectedDate.toDateString()}</p>
</div>
);
}Key point: The value prop holds a single Date object. Update via the change event.
---
Multiple Date Selection
Use the isMultiSelection and values props to enable native multiple date selection. The change event returns args.values (array) when multi-selection is active.
import React, { useState } from 'react';
import { CalendarComponent } from '@syncfusion/ej2-react-calendars';
export default function MultiDateExample() {
const [selectedDates, setSelectedDates] = useState([
new Date(2026, 10, 5),
new Date(2026, 10, 12),
]);
const onChange = (args) => {
// args.values contains the updated Date[] array
if (args.values) {
setSelectedDates(args.values);
}
};
return (
<div style={{ padding: 20 }}>
<h3>Pick multiple dates</h3>
<CalendarComponent
isMultiSelection={true}
values={selectedDates}
change={onChange}
/>
<p>Selected dates:</p>
<ul>
{selectedDates.map((d, i) => (
<li key={i}>{d.toDateString()}</li>
))}
</ul>
</div>
);
}Key points:
- Set
isMultiSelection={true}to enable the built-in multi-date selection mode. - Use the
valuesprop (notvalue) to provide the initial selection array. - In the
changehandler, readargs.valuesto get the full updated array. - To add/remove dates imperatively, use the
addDate()andremoveDate()methods via a ref.
---
Date Range Selection
For selecting a date range (start–end), use conditional state to track both dates.
import React, { useState } from 'react';
import { CalendarComponent } from '@syncfusion/ej2-react-calendars';
export default function DateRangeExample() {
const [startDate, setStartDate] = useState(null);
const [endDate, setEndDate] = useState(null);
const onChange = (args) => {
if (!startDate || (startDate && endDate)) {
// First click or reset: set start date
setStartDate(args.value);
setEndDate(null);
} else {
// Second click: set end date
const start = startDate;
const end = args.value;
if (end < start) {
setStartDate(end);
setEndDate(start);
} else {
setStartDate(start);
setEndDate(end);
}
}
};
return (
<div style={{ padding: 20 }}>
<h3>Select a date range</h3>
<CalendarComponent value={startDate} change={onChange} />
<p>
Start: {startDate ? startDate.toDateString() : 'Not selected'}
<br />
End: {endDate ? endDate.toDateString() : 'Not selected'}
</p>
<button onClick={() => { setStartDate(null); setEndDate(null); }}>
Reset
</button>
</div>
);
}For a more polished UX with visual range highlighting, use the renderDayCell hook (see events-methods.md).
---
Min/Max Date Constraints
Restrict selectable dates to a given range.
import React, { useState } from 'react';
import { CalendarComponent } from '@syncfusion/ej2-react-calendars';
export default function ConstrainedExample() {
const [value, setValue] = useState(new Date());
const minDate = new Date(2026, 0, 1); // Jan 1, 2026
const maxDate = new Date(2026, 11, 31); // Dec 31, 2026
return (
<div style={{ padding: 20 }}>
<h3>Pick a date in 2026</h3>
<CalendarComponent
value={value}
min={minDate}
max={maxDate}
change={(e) => setValue(e.value)}
/>
<p>Selected: {value.toDateString()}</p>
</div>
);
}Result: Dates outside the min/max range appear disabled (grayed out), and users cannot click them.
---
Disabling Specific Dates
Use renderDayCell to disable individual dates (e.g., weekends, holidays).
import React, { useState } from 'react';
import { CalendarComponent } from '@syncfusion/ej2-react-calendars';
export default function DisableDatesExample() {
const [value, setValue] = useState(new Date());
// Disable weekends and specific holidays
const holidayDates = [
new Date(2026, 11, 25), // Christmas
];
const renderDayCell = (args) => {
const date = args.date;
const dayOfWeek = date.getDay();
// Disable weekends
if (dayOfWeek === 0 || dayOfWeek === 6) {
args.isDisabled = true;
}
// Disable holidays
if (holidayDates.some(h => h.toDateString() === date.toDateString())) {
args.isDisabled = true;
}
};
return (
<div style={{ padding: 20 }}>
<h3>Weekends and holidays disabled</h3>
<CalendarComponent
value={value}
change={(e) => setValue(e.value)}
renderDayCell={renderDayCell}
/>
</div>
);
}---
Reading Selection State
Direct from the component (using ref)
import React, { useRef } from 'react';
import { CalendarComponent } from '@syncfusion/ej2-react-calendars';
export default function ReadStateExample() {
const calendarRef = useRef(null);
const getSelected = () => {
if (calendarRef.current) {
console.log('Current value:', calendarRef.current.value);
}
};
return (
<div>
<CalendarComponent ref={calendarRef} />
<button onClick={getSelected}>Get Selected Date</button>
</div>
);
}Via React state (recommended)
Always keep React state in sync with the component via the change event, as shown in the single-date example above. This is the idiomatic React pattern.
---
Best Practices
1. Always use React state to track the selected date(s) so your component re-renders correctly. 2. Use `renderDayCell` for complex logic like disabling specific dates or adding custom styling. 3. Validate dates before storing or sending to the backend (check min/max, format, etc.). 4. Provide feedback via p tags or alerts when a user selects an invalid date. 5. Test edge cases like the 29th of February, month boundaries, and timezone differences.
---
Common Pitfalls
- Not syncing React state: If you update only the component's internal value without updating React state, re-renders may not reflect the new selection.
- Comparing dates as strings inconsistently: Always use
.toDateString()or timestamp comparison for consistency. - Forgetting to disable the selected date in range mode: After the user picks a start date, you may want to disable that date in subsequent picks to avoid start === end.
Events and Methods
Table of Contents
- Calendar Events
- Event Handlers (Examples)
- Calendar Methods
- navigateTo
- currentView
- addDate
- removeDate
- destroy
- getPersistData
- Using Refs for Imperative Control
- Advanced: renderDayCell Hook
---
Calendar Events
change
Fired when the user selects a date or the selected date changes programmatically.
Handler signature:
(args: { value: Date }) => voidExample:
const onChange = (args) => {
console.log('Selected date:', args.value);
};
<CalendarComponent change={onChange} />created
Fired after the Calendar component is initialized and rendered.
Handler signature:
() => voidUse case: Initialize component-dependent logic, fetch data for the selected month, etc.
const onCreated = () => {
console.log('Calendar initialized');
};
<CalendarComponent created={onCreated} />destroyed
Fired when the component is about to be removed from the DOM (cleanup phase).
Handler signature:
() => voidUse case: Cleanup timers, event listeners, or external resources.
const onDestroyed = () => {
console.log('Calendar destroyed, cleanup resources');
};
<CalendarComponent destroyed={onDestroyed} />navigated
Fired after the view navigates (e.g., Month → Year view).
Handler signature (may vary by version):
(args: any) => voidExample:
const onNavigated = (args) => {
console.log('Navigation event:', args);
};
<CalendarComponent navigated={onNavigated} />renderDayCell
Hook for customizing individual day cells before they are rendered. Allows you to:
- Disable specific dates
- Add custom styling
- Add visual indicators (badges, highlights)
Handler signature:
(args: { date: Date; cellElement: HTMLElement; isDisabled?: boolean }) => voidSee the [Advanced: renderDayCell Hook](#advanced-renderdaycell-hook) section below for detailed examples.
---
Event Handlers (Examples)
Example 1: Track Selection and Display
import React, { useState } from 'react';
import { CalendarComponent } from '@syncfusion/ej2-react-calendars';
export default function EventTrackingExample() {
const [selectedDate, setSelectedDate] = useState(new Date());
const [eventLog, setEventLog] = useState([]);
const onChange = (args) => {
const newEntry = `Selected: ${args.value.toDateString()}`;
setSelectedDate(args.value);
setEventLog([...eventLog, newEntry]);
};
const onCreated = () => {
setEventLog(['Calendar initialized']);
};
return (
<div style={{ padding: 20 }}>
<CalendarComponent
change={onChange}
created={onCreated}
/>
<h3>Current: {selectedDate.toDateString()}</h3>
<h4>Event Log:</h4>
<ul>
{eventLog.map((log, i) => <li key={i}>{log}</li>)}
</ul>
</div>
);
}Example 2: Detect Month Changes
import React, { useState, useRef } from 'react';
import { CalendarComponent } from '@syncfusion/ej2-react-calendars';
export default function MonthChangeDetectorExample() {
const [currentMonth, setCurrentMonth] = useState(new Date());
const prevMonthRef = useRef(new Date().getMonth());
const onChange = (args) => {
const newMonth = args.value.getMonth();
if (newMonth !== prevMonthRef.current) {
console.log('Month changed from', prevMonthRef.current, 'to', newMonth);
prevMonthRef.current = newMonth;
}
setCurrentMonth(args.value);
};
return (
<div style={{ padding: 20 }}>
<CalendarComponent change={onChange} />
<p>Current: {currentMonth.toLocaleString('default', { month: 'long', year: 'numeric' })}</p>
</div>
);
}---
Calendar Methods
Call these methods via a ref to the CalendarComponent instance.
navigateTo(view, date, isCustomDate?)
Navigate the calendar to a specific view and date.
Signature: navigateTo(view: CalendarView, date: Date, isCustomDate?: boolean): void
view— Target view:'Month','Year', or'Decade'.date— The focused date in that view.isCustomDate_(optional)_ — Whether the calendar is rendered with a custom today date.
import React, { useRef } from 'react';
import { CalendarComponent } from '@syncfusion/ej2-react-calendars';
export default function NavigateToExample() {
const calendarRef = useRef(null);
const jumpToDate = (view, year, month, day) => {
if (calendarRef.current) {
calendarRef.current.navigateTo(view, new Date(year, month - 1, day));
}
};
return (
<div style={{ padding: 20 }}>
<CalendarComponent ref={calendarRef} />
<div style={{ marginTop: 20 }}>
<button onClick={() => jumpToDate('Month', 2026, 1, 1)}>Jan 2026</button>
<button onClick={() => jumpToDate('Month', 2026, 7, 15)}>July 2026</button>
<button onClick={() => jumpToDate('Year', 2025, 1, 1)}>Year 2025</button>
</div>
</div>
);
}currentView()
Get the current view type as a string.
const getView = () => {
if (calendarRef.current) {
const view = calendarRef.current.currentView();
console.log('Current view:', view); // 'Month', 'Year', or 'Decade'
}
};
<button onClick={getView}>Check View</button>addDate(dates)
Adds one or multiple dates to the values property (for multi-selection).
Signature: addDate(dates: Date | Date[]): void
const addDates = () => {
calendarRef.current?.addDate([new Date(2026, 10, 5), new Date(2026, 10, 10)]);
};removeDate(dates)
Removes one or multiple dates from the values property.
Signature: removeDate(dates: Date | Date[]): void
const removeDates = () => {
calendarRef.current?.removeDate(new Date(2026, 10, 5));
};destroy()
Destroys the widget and releases all resources.
Signature: destroy(): void
const destroyCalendar = () => {
calendarRef.current?.destroy();
};getPersistData()
Returns the properties to be maintained upon browser refresh (used with enablePersistence).
Signature: getPersistData(): string
const persistData = calendarRef.current?.getPersistData();
console.log(persistData);---
Using Refs for Imperative Control
Complete Ref Example
import React, { useRef, useState } from 'react';
import { CalendarComponent } from '@syncfusion/ej2-react-calendars';
export default function ImperativeControlExample() {
const calendarRef = useRef(null);
const [status, setStatus] = useState('');
const handleNavigate = (offset) => {
if (calendarRef.current && calendarRef.current.value) {
const newDate = new Date(calendarRef.current.value);
newDate.setMonth(newDate.getMonth() + offset);
// navigateTo(view, date)
calendarRef.current.navigateTo('Month', newDate);
setStatus(`Navigated to ${newDate.toLocaleString('default', { month: 'long', year: 'numeric' })}`);
}
};
const handleGetInfo = () => {
if (calendarRef.current) {
const view = calendarRef.current.currentView();
const value = calendarRef.current.value;
setStatus(`View: ${view}, Date: ${value?.toDateString() || 'None'}`);
}
};
return (
<div style={{ padding: 20 }}>
<CalendarComponent ref={calendarRef} />
<div style={{ marginTop: 20 }}>
<button onClick={() => handleNavigate(-1)}>← Previous Month</button>
<button onClick={() => handleNavigate(1)}>Next Month →</button>
<button onClick={handleGetInfo}>Get Info</button>
</div>
<p style={{ marginTop: 10, color: '#0066cc' }}>{status}</p>
</div>
);
}---
Advanced: renderDayCell Hook
The renderDayCell callback is invoked for every day cell before rendering. Use it to customize appearance and behavior.
Parameters
args.date— TheDateobject for the cell.args.cellElement— The DOM element representing the cell.args.isDisabled— Boolean; set totrueto disable the cell.
Example: Disable Weekends
const renderDayCell = (args) => {
const dayOfWeek = args.date.getDay();
if (dayOfWeek === 0 || dayOfWeek === 6) {
args.isDisabled = true;
}
};
<CalendarComponent renderDayCell={renderDayCell} />Example: Highlight Today and Add Inline Styles
const renderDayCell = (args) => {
const today = new Date();
today.setHours(0, 0, 0, 0);
if (args.date.toDateString() === today.toDateString()) {
args.cellElement.style.backgroundColor = '#ffc107';
args.cellElement.style.fontWeight = 'bold';
}
};
<CalendarComponent renderDayCell={renderDayCell} />Example: Add CSS Class for Styling
const renderDayCell = (args) => {
const eventDates = ['2026-03-15', '2026-03-25'];
const dateStr = args.date.toISOString().split('T')[0];
if (eventDates.includes(dateStr)) {
args.cellElement.classList.add('has-event');
}
};
// In your CSS file:
// .e-calendar .e-cell.has-event {
// background-color: #e7f3ff;
// border: 2px solid #0066ff;
// }
<CalendarComponent renderDayCell={renderDayCell} />Example: Complex Logic (Disable Past + Future Range)
const renderDayCell = (args) => {
const today = new Date();
today.setHours(0, 0, 0, 0);
const rangeStart = new Date(2026, 0, 1); // Jan 1, 2026
const rangeEnd = new Date(2026, 11, 31); // Dec 31, 2026
// Disable if before today
if (args.date < today) {
args.isDisabled = true;
}
// Disable if outside range
else if (args.date < rangeStart || args.date > rangeEnd) {
args.isDisabled = true;
}
// Highlight weekends in the valid range
else if (args.date.getDay() === 0 || args.date.getDay() === 6) {
args.cellElement.style.backgroundColor = '#f0f0f0';
}
};
<CalendarComponent renderDayCell={renderDayCell} />---
Best Practices
1. Keep event handlers lightweight — avoid heavy computations in change or renderDayCell. 2. Use refs sparingly — prefer declarative React patterns (props + state) when possible. 3. Cleanup in `destroyed` — if you attach external event listeners in created, remove them in destroyed. 4. Cache expensive computations — in renderDayCell, compute disabled dates once outside the callback (not per cell). 5. Use `addDate`/`removeDate` for multi-selection — these methods are the idiomatic way to manage the values array imperatively. 6. Always pass both arguments to `navigateTo` — the method requires both view (CalendarView) and date (Date).
Getting Started (React)
Table of Contents
Installation
Install the React Calendar package and base utilities:
npm install @syncfusion/ej2-react-calendars @syncfusion/ej2-baseAdd the theme CSS (import once in index.js or global CSS):
// index.js
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';
import '@syncfusion/ej2-base/styles/material3.css';
import '@syncfusion/ej2-calendars/styles/material3.css';
createRoot(document.getElementById('root')).render(<App />);Quick App Example
// App.jsx
import React, { useState } from 'react';
import { CalendarComponent } from '@syncfusion/ej2-react-calendars';
export default function App() {
const [value, setValue] = useState(new Date());
const onChange = (args) => setValue(args.value || args);
return (
<div style={{ padding: 20 }}>
<h2>React Calendar</h2>
<CalendarComponent value={value} change={onChange} />
<p>Selected: {value.toDateString()}</p>
</div>
);
}Controlled vs Uncontrolled
- Controlled: pass
valueand update it via thechangeevent. - Uncontrolled: omit
valueand read selected date via event callbacks or methods.
CSS / Themes
- Recommended to import one theme only (material3, bootstrap5, fluent, etc.).
- When using CSS modules or scoped styles, ensure global imports occur before component styles.
Using refs and methods
You can obtain a component reference to call imperative methods (navigate, focus, etc.).
import React, { useRef } from 'react';
import { CalendarComponent } from '@syncfusion/ej2-react-calendars';
export default function RefExample() {
const calendarRef = useRef(null);
const goToNext = () => {
if (calendarRef.current) {
// navigateTo(view: CalendarView, date: Date)
calendarRef.current.navigateTo('Month', new Date(2026, 6, 1));
}
};
return (
<div>
<CalendarComponent ref={calendarRef} />
<button onClick={goToNext}>Go to July 2026</button>
</div>
);
}Note: method names and available APIs are listed in the API reference file.
Events and handlers
change— user changed the active/selected date. Receives an args object withvalue.created— fired after the component is initialized.destroyed— fired when the component is removed.renderDayCell— hook to customize day cells before rendering.
Examples:
const onChange = (args) => {
console.log('selected:', args.value);
};
<CalendarComponent change={onChange} created={() => console.log('ready')} />Troubleshooting
- "Styles not applied": check import paths and ensure the CSS is included in the bundle.
- "Change not firing": ensure
changeprop is supplied; for controlled components ensurevalueis updated from state. - "Cannot find module": reinstall packages and clear
node_modulesif necessary.
Next steps
- Read
references/api-reference.mdfor a condensed list of props, events, and methods. - For date pickers and more complex date-range UX, consider
DateRangePickerorDatePickercomponents in the same library.
Styling and Customization
Table of Contents
- Theme Selection
- CSS Class Customization
- Custom Day Cell Rendering
- RTL Support
- CSS Variables and Overrides
- Responsive Design
---
Theme Selection
Syncfusion provides multiple built-in themes. Choose one and import it once (preferably in your main index.js or global styles).
Available Themes
material3.css— Material Design 3 (modern, default)bootstrap5.css— Bootstrap 5 stylingfluent.css— Microsoft Fluent Designtailwind.css— Tailwind CSS themefabric.css— Office Fabric theme
Import in index.js
// index.js
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';
// Import theme once
import '@syncfusion/ej2-base/styles/material3.css';
import '@syncfusion/ej2-calendars/styles/material3.css';
createRoot(document.getElementById('root')).render(<App />);Switch Themes Dynamically
To change themes at runtime, dynamically add/remove <link> tags:
import React, { useState } from 'react';
import { CalendarComponent } from '@syncfusion/ej2-react-calendars';
export default function ThemeSwitcher() {
const [theme, setTheme] = useState('material3');
const switchTheme = (newTheme) => {
// Remove old theme link
const oldLink = document.querySelector(`link[data-theme="${theme}"]`);
if (oldLink) oldLink.remove();
// Add new theme link
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = `/node_modules/@syncfusion/ej2-calendars/styles/${newTheme}.css`;
link.dataset.theme = newTheme;
document.head.appendChild(link);
setTheme(newTheme);
};
return (
<div style={{ padding: 20 }}>
<div>
<button onClick={() => switchTheme('material3')}>Material3</button>
<button onClick={() => switchTheme('bootstrap5')}>Bootstrap5</button>
<button onClick={() => switchTheme('fluent')}>Fluent</button>
</div>
<CalendarComponent />
</div>
);
}---
CSS Class Customization
Using the cssClass prop
Add custom CSS classes to the calendar root element:
import React, { useState } from 'react';
import { CalendarComponent } from '@syncfusion/ej2-react-calendars';
import './CustomCalendar.css';
export default function CustomCSSExample() {
const [value, setValue] = useState(new Date());
return (
<div style={{ padding: 20 }}>
<CalendarComponent
value={value}
change={(e) => setValue(e.value)}
cssClass="my-custom-calendar"
/>
</div>
);
}CustomCalendar.css:
.my-custom-calendar {
border: 2px solid #007bff;
border-radius: 8px;
padding: 10px;
}
.my-custom-calendar .e-title {
background-color: #007bff;
color: white;
padding: 10px;
border-radius: 4px;
}
.my-custom-calendar .e-cell {
font-size: 14px;
font-weight: 500;
}
.my-custom-calendar .e-selected {
background-color: #28a745;
color: white;
}---
Custom Day Cell Rendering
Use the renderDayCell callback to customize the appearance and behavior of individual day cells.
Example: Highlight Weekends
import React, { useState } from 'react';
import { CalendarComponent } from '@syncfusion/ej2-react-calendars';
export default function HighlightWeekendsExample() {
const [value, setValue] = useState(new Date());
const renderDayCell = (args) => {
const dayOfWeek = args.date.getDay();
if (dayOfWeek === 0 || dayOfWeek === 6) {
args.cellElement.style.backgroundColor = '#f0f0f0';
args.cellElement.style.color = '#999';
}
};
return (
<div style={{ padding: 20 }}>
<CalendarComponent
value={value}
change={(e) => setValue(e.value)}
renderDayCell={renderDayCell}
/>
</div>
);
}Example: Add Badge for Special Dates
import React, { useState } from 'react';
import { CalendarComponent } from '@syncfusion/ej2-react-calendars';
export default function BadgeExample() {
const [value, setValue] = useState(new Date());
const eventDates = [
new Date(2026, 2, 15), // March 15
new Date(2026, 2, 25), // March 25
];
const renderDayCell = (args) => {
const dateStr = args.date.toDateString();
const isEvent = eventDates.some((d) => d.toDateString() === dateStr);
if (isEvent) {
// Create a badge overlay
const badge = document.createElement('span');
badge.textContent = '●';
badge.style.color = 'red';
badge.style.fontSize = '16px';
badge.style.position = 'absolute';
badge.style.top = '2px';
badge.style.right = '2px';
args.cellElement.style.position = 'relative';
args.cellElement.appendChild(badge);
}
};
return (
<div style={{ padding: 20 }}>
<CalendarComponent
value={value}
change={(e) => setValue(e.value)}
renderDayCell={renderDayCell}
/>
</div>
);
}Example: Disable Past Dates
const renderDayCell = (args) => {
const today = new Date();
today.setHours(0, 0, 0, 0);
if (args.date < today) {
args.isDisabled = true;
args.cellElement.style.opacity = '0.5';
}
};
<CalendarComponent renderDayCell={renderDayCell} />---
RTL Support
Enable right-to-left text direction for Arabic, Hebrew, and other RTL languages.
import React, { useState } from 'react';
import { CalendarComponent } from '@syncfusion/ej2-react-calendars';
export default function RTLExample() {
const [value, setValue] = useState(new Date());
return (
<div style={{ padding: 20, direction: 'rtl' }}>
<CalendarComponent
value={value}
change={(e) => setValue(e.value)}
enableRtl={true}
locale="ar" // Arabic locale
/>
</div>
);
}Key points:
- Set
enableRtl={true}on the component. - Set
direction: 'rtl'on the parent container in CSS or inline. - Use appropriate locale code (e.g.,
'ar'for Arabic,'he'for Hebrew).
---
CSS Variables and Overrides
Syncfusion components often expose CSS variables for theming. You can override them in your CSS:
:root {
--e-primary-color: #007bff;
--e-body-font: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
--e-border-radius: 4px;
}
/* Or specifically for calendar */
.e-calendar {
--e-selected-bg-color: #28a745;
--e-hover-bg-color: #e9ecef;
}Then apply those variables in your global styles or component CSS files. This allows you to theme multiple components consistently.
---
Responsive Design
The Calendar adapts to different screen sizes automatically, but you can enhance responsiveness:
import React, { useState } from 'react';
import { CalendarComponent } from '@syncfusion/ej2-react-calendars';
export default function ResponsiveExample() {
const [value, setValue] = useState(new Date());
const getCalendarStyles = () => {
const width = window.innerWidth;
if (width < 600) {
return { width: '100%', maxWidth: '300px' };
} else if (width < 1200) {
return { width: '400px' };
}
return { width: '500px' };
};
return (
<div style={{ padding: 20, ...getCalendarStyles() }}>
<CalendarComponent
value={value}
change={(e) => setValue(e.value)}
/>
</div>
);
}Mobile-Friendly Tips
1. Touch targets: Ensure day cells are large enough to tap on mobile (minimum 44x44 px). 2. Font size: Increase font size on small screens for readability. 3. Container width: Constrain the calendar width so it doesn't overflow on phones.
@media (max-width: 600px) {
.e-calendar {
font-size: 16px;
}
.e-calendar .e-cell {
height: 50px;
line-height: 50px;
}
}
@media (min-width: 601px) {
.e-calendar {
font-size: 14px;
}
}---
Best Practices
1. Import CSS once: Add theme imports to your main index.js, not in every component. 2. Use `cssClass` for scoped changes: Keeps customizations organized and reusable. 3. Avoid inline styles in `renderDayCell`: Instead, add CSS classes and style them externally. 4. Test RTL: If supporting RTL, verify layout on mobile and desktop. 5. Use CSS variables: For consistent theming across multiple components and screens.
---
Common Issues
- Styles not loading: Ensure the CSS import path is correct relative to
node_modules. - RTL not working: Remember to set
enableRtl={true}anddirection: 'rtl'on the parent container. - Custom CSS not applying: Check CSS specificity; you may need
!importantfor overrides, though avoid if possible.
Accessibility & Keyboard Navigation
Table of Contents
- WCAG 2.2 Compliance
- Keyboard Navigation
- ARIA Attributes
- Screen Reader Support
- Focus Management
- Color Contrast
- Mobile Accessibility
- Testing Accessibility
WCAG 2.2 Compliance
DatePicker meets WCAG 2.2 level AA standards:
Supported Standards
| Standard | Status | Details |
|---|---|---|
| WCAG 2.2 Level AA | ✅ Supported | Most features compliant |
| Section 508 (US Law) | ✅ Partial | Key features supported |
| ADA (Americans with Disabilities) | ✅ Supported | Accessible to all users |
| ARIA 1.2 Specification | ✅ Supported | WAI-ARIA patterns implemented |
Key WCAG Principles
1. Perceivable - Content visible to all (text, color, icons) 2. Operable - Keyboard navigation, sufficient time, no seizure triggers 3. Understandable - Clear labels, predictable behavior, error prevention 4. Robust - Compatible with assistive technologies
Keyboard Navigation
Input Field (Before Calendar Opens)
| Key | Action |
|---|---|
| Alt + Down Arrow | Open calendar popup |
| Alt + Up Arrow | Close calendar popup |
| Escape | Close calendar (if open) |
| Tab | Move focus to next element |
| Shift + Tab | Move focus to previous element |
Calendar Navigation (Month View)
| Key | Action |
|---|---|
| Arrow Up | Focus previous week date |
| Arrow Down | Focus next week date |
| Arrow Left | Focus previous day |
| Arrow Right | Focus next day |
| Home | Focus first date in month |
| End | Focus last date in month |
| Page Up | Previous month |
| Page Down | Next month |
| Enter | Select focused date, close calendar |
| Escape | Close calendar without selecting |
| Space | Select focused date |
Calendar Navigation (Year View)
| Key | Action |
|---|---|
| Arrow Keys | Move between months |
| Page Up | Previous year |
| Page Down | Next year |
| Home | First month (January) |
| End | Last month (December) |
| Enter | Select month, go to Month view |
Calendar Navigation (Decade View)
| Key | Action |
|---|---|
| Arrow Keys | Move between years |
| Page Up | Previous decade |
| Page Down | Next decade |
| Home | First year of decade |
| End | Last year of decade |
| Enter | Select year, go to Year view |
Complete Keyboard Journey
1. User focuses DatePicker input
2. User presses Tab to reach DatePicker
3. User presses Alt+Down to open calendar
4. DatePicker opens in Month view with today highlighted
5. User presses Up Arrow 3 times to go back 3 weeks
6. User presses Right Arrow 2 times to select 2 days forward
7. User presses Enter to select and closeARIA Attributes
DatePicker automatically applies accessibility attributes:
Applied to Input Element
| Attribute | Value | Purpose |
|---|---|---|
aria-label | "DatePicker" | Identifies purpose to screen readers |
aria-expanded | true/false | Indicates calendar open/closed state |
aria-disabled | true/false | Indicates disabled state |
aria-readonly | true/false | Indicates readonly state |
aria-activedescendant | ID of focused cell | Indicates currently focused day |
role | "textbox" | Identifies as input element |
Applied to Calendar Popup
| Attribute | Value | Purpose |
|---|---|---|
role | "dialog" | Popup is a modal dialog |
aria-modal | true | Only popup can be interacted with |
aria-label | Month/Year | Shows current view context |
aria-hidden | false | Popup is visible to screen readers |
Applied to Day Cells
| Attribute | Value | Purpose |
|---|---|---|
aria-disabled | true/false | Indicates if day is selectable |
aria-selected | true/false | Indicates if day is selected |
role | "button" | Accessible as clickable button |
aria-label | "March 19, 2026" | Full date description |
Example: Checking ARIA in Browser DevTools
<!-- Open calendar in browser DevTools Elements panel -->
<input
aria-label="DatePicker"
aria-expanded="true"
aria-activedescendant="datepicker_cell_19"
role="textbox"
/>
<!-- Calendar popup -->
<div role="dialog" aria-modal="true" aria-label="March 2026">
<button
id="datepicker_cell_19"
role="button"
aria-disabled="false"
aria-selected="false"
aria-label="Thursday, March 19, 2026"
>
19
</button>
</div>Screen Reader Support
Testing with NVDA (Windows)
1. Install NVDA: https://www.nvaccess.org/ 2. Open browser with DatePicker page 3. Press Ctrl+Alt+N to start NVDA 4. Tab to DatePicker input 5. NVDA announces: "DatePicker input, collapsed" 6. Press Alt+Down to open calendar 7. NVDA announces: "Calendar popup, March 2026, Month view" 8. Navigate with arrows, NVDA announces each date
Testing with JAWS (Windows)
1. Tab to DatePicker input 2. JAWS reads: "Date picker textbox, collapsed" 3. Press Alt+Down to open 4. JAWS announces: "Calendar opened, March 2026" 5. Use arrow keys, JAWS reads each date
Testing with VoiceOver (Mac)
1. Enable VoiceOver: Cmd+F5 2. Tab to DatePicker input 3. VoiceOver announces purpose and state 4. Press VO+Space to open calendar (may vary) 5. Navigate with arrow keys
What Screen Reader Users Hear
Default state:
"Date Picker input, collapsed, select to open calendar"After opening calendar:
"Calendar dialog, March 2026, Month view.
Currently focused on today, March 19, 2026.
Highlighted as current selection."Navigating with arrow keys:
(Down Arrow)
"Friday, March 20, 2026, not selected, clickable"
(Down Arrow)
"Friday, March 27, 2026, disabled, outside range"Focus Management
Default Focus Behavior
1. Initial Focus: Today's date when calendar opens 2. Selection Focus: After selecting, focus returns to input 3. Tab Order: DatePicker fits naturally in page tab order
Visible Focus Indicator
/* Focus outline is visible on input */
.e-datepicker:focus {
outline: 2px solid #0066cc;
outline-offset: 2px;
}
/* Focus visible on calendar day */
.e-calendar .e-day:focus {
box-shadow: inset 0 0 0 2px #0066cc;
}Managing Focus Programmatically
import React, { useRef } from 'react';
import { DatePickerComponent } from '@syncfusion/ej2-react-calendars';
export default function App() {
const datePickerRef = useRef(null);
const handleFocus = () => {
// Focus the DatePicker using focusIn() method
if (datePickerRef.current) {
datePickerRef.current.focusIn();
}
};
const handleBlur = () => {
// Remove focus from DatePicker using focusOut() method
if (datePickerRef.current) {
datePickerRef.current.focusOut();
}
};
const handleOpen = () => {
// Open calendar using show() method
if (datePickerRef.current) {
datePickerRef.current.show();
}
};
const handleClose = () => {
// Close calendar using hide() method
if (datePickerRef.current) {
datePickerRef.current.hide();
}
};
return (
<div>
<button onClick={handleFocus}>Focus DatePicker</button>
<button onClick={handleBlur}>Remove Focus</button>
<button onClick={handleOpen}>Open Calendar</button>
<button onClick={handleClose}>Close Calendar</button>
<DatePickerComponent
ref={datePickerRef}
value={new Date()}
placeholder="Select date"
focus={(e) => console.log('Focused:', e)}
blur={(e) => console.log('Blurred:', e)}
open={(e) => console.log('Calendar opened')}
close={(e) => console.log('Calendar closed')}
change={(e) => console.log('Value changed:', e.value)}
navigated={(e) => console.log('Navigated to:', e.view)}
/>
</div>
);
}Color Contrast
Compliance with WCAG Standards
DatePicker meets color contrast requirements:
| Element | Foreground | Background | Ratio | WCAG Level |
|---|---|---|---|---|
| Text | #333333 | #FFFFFF | 12.6:1 | AAA |
| Selected Date | #FFFFFF | #0066cc | 8.6:1 | AAA |
| Disabled Date | #999999 | #FFFFFF | 4.5:1 | AA |
| Hover | #0066cc | #F0F0F0 | 5.5:1 | AA |
Testing Color Contrast
Use WebAIM Contrast Checker: https://webaim.org/resources/contrastchecker/
Foreground: #333333 (Text)
Background: #FFFFFF (White)
Contrast Ratio: 12.6:1 ✅ WCAG AAA PassHigh Contrast Mode Support
For users with vision impairments, DatePicker supports high contrast:
import '@syncfusion/ej2-react-calendars/styles/highcontrast.css';
<DatePickerComponent value={new Date()} />High Contrast Theme:
- Increased contrast ratios (typically >7:1)
- Stronger borders and outlines
- No reliance on color alone
- Supports Windows High Contrast mode
Mobile Accessibility
Touch Keyboard Navigation
On mobile devices, DatePicker adapts:
- Touch opens calendar popup
- Touch keyboard for text input (supports ime)
- Touch-friendly calendar grid (larger touch targets)
- Calendar layout optimized for small screens
Mobile Testing
<DatePickerComponent
value={new Date()}
placeholder="Select date"
// Mobile automatically adjusts layout
/>Mobile user experience: 1. Focus DatePicker input 2. Tap to open calendar 3. Calendar appears at appropriate size 4. Touch dates to select 5. Calendar closes, date populates
Responsive Sizing
/* Automatically responsive */
.e-datepicker {
width: 100%;
max-width: 100%;
}
@media (max-width: 600px) {
.e-popup-wrapper {
width: 100vw;
height: auto;
}
}Testing Accessibility
Automated Testing
Use tools to check accessibility:
# Install accessibility checker
npm install axe-core --save-devimport { axe, toHaveNoViolations } from 'jest-axe';
test('DatePicker has no accessibility violations', async () => {
const { container } = render(
<DatePickerComponent value={new Date()} />
);
const results = await axe(container);
expect(results).toHaveNoViolations();
});Manual Testing Checklist
Keyboard Navigation:
☐ Can reach DatePicker with Tab
☐ Alt+Down opens calendar
☐ Arrow keys navigate dates
☐ Enter selects date
☐ Escape closes without selecting
☐ Tab order is logical
Screen Reader:
☐ NVDA announces purpose
☐ JAWS reads state correctly
☐ VoiceOver provides feedback
☐ Dates announced with full description
☐ Disabled dates announced
Focus Visible:
☐ Focus outline visible on all focused elements
☐ Focus outline has sufficient contrast
☐ Focus indicator not removed
Color & Contrast:
☐ Text readable without color reliance
☐ Links and buttons distinguishable
☐ Color contrast >4.5:1 for normal text
☐ Color contrast >7:1 for large text
Mobile:
☐ Touch targets sufficient size (44x44px minimum)
☐ No horizontal scrolling required
☐ Zoom functionality works
☐ Landscape and portrait workComplete Accessibility Example
import React, { useState } from 'react';
import { DatePickerComponent } from '@syncfusion/ej2-react-calendars';
import '@syncfusion/ej2-react-calendars/styles/highcontrast.css';
export default function AccessibleApp() {
const [date, setDate] = useState(new Date());
return (
<div style={{ padding: '20px' }}>
<h1>Accessible DatePicker Form</h1>
<form>
<fieldset>
<legend>Event Registration</legend>
<div>
<label htmlFor="event-date">
<strong>Event Date:</strong>
<span style={{ color: 'red' }}>*</span>
<span className="sr-only"> (Required)</span>
</label>
<DatePickerComponent
id="event-date"
value={date}
change={(e) => setDate(e.value)}
min={new Date()}
max={new Date(Date.now() + 365 * 24 * 60 * 60 * 1000)}
placeholder="Choose an event date (YYYY-MM-DD)"
format="yyyy-MM-dd"
aria-required="true"
aria-describedby="date-help"
/>
<small id="date-help" style={{ display: 'block', marginTop: '5px' }}>
Select a date within the next year.
</small>
</div>
<button type="submit">Register Event</button>
</fieldset>
</form>
<style>{`
.sr-only {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
}
`}</style>
</div>
);
}Date Formats & Input
Table of Contents
- Display Format
- Format Pattern Syntax
- Common Format Examples
- Input Formats
- Automatic Format Conversion
- Culture-Based Formatting
- Format and Input Formats Together
Display Format
The format property controls how the selected date is displayed in the input field.
Default Format
By default, DatePicker uses the culture's default format:
<DatePickerComponent
value={new Date()}
// format not specified, uses culture default (e.g., "M/d/yyyy" for en-US)
/>Custom Format
Set a custom display format using the format property:
<DatePickerComponent
id="datepicker"
value={new Date()}
format="yyyy-MM-dd" // ISO format: 2026-03-19
placeholder="YYYY-MM-DD"
/>When user selects March 19, 2026:
- Input shows:
2026-03-19 - The format applies only to display, not the underlying Date object
Format Pattern Syntax
DatePicker uses this pattern syntax for custom formats:
| Character | Meaning | Example |
|---|---|---|
y or yy | 2-digit year | 26 |
yyyy | 4-digit year | 2026 |
M | Month without leading zero | 3 (for March) |
MM | Month with leading zero | 03 (for March) |
MMM | Month abbreviation | Mar |
MMMM | Full month name | March |
d | Day without leading zero | 9 |
dd | Day with leading zero | 09 |
ddd | Day abbreviation | Thu |
dddd | Full day name | Thursday |
/ or - | Separator (literal) | - or / |
Pattern Construction
Combine characters to build formats:
yyyy-MM-dd → 2026-03-19
dd/MM/yyyy → 19/03/2026
MMMM d, yyyy → March 19, 2026
dddd, MMMM d → Thursday, March 19
MM/dd/yy → 03/19/26
d MMM yyyy → 19 Mar 2026Common Format Examples
US Format (Month-Day-Year)
<DatePickerComponent
value={new Date()}
format="MM/dd/yyyy" // 03/19/2026
placeholder="MM/DD/YYYY"
/>European Format (Day-Month-Year)
<DatePickerComponent
value={new Date()}
format="dd/MM/yyyy" // 19/03/2026
placeholder="DD/MM/YYYY"
/>ISO Format (Year-Month-Day)
<DatePickerComponent
value={new Date()}
format="yyyy-MM-dd" // 2026-03-19
placeholder="YYYY-MM-DD"
/>Long Format with Day Name
<DatePickerComponent
value={new Date()}
format="dddd, MMMM d, yyyy" // Thursday, March 19, 2026
placeholder="Day, Month Date, Year"
/>Short Format with Month Name
<DatePickerComponent
value={new Date()}
format="d MMM yyyy" // 19 Mar 2026
placeholder="D Mon YYYY"
/>Input Formats
The inputFormats property lets users enter dates in multiple formats, which are automatically converted to the display format.
Single Input Format
<DatePickerComponent
value={new Date()}
format="yyyy-MM-dd" // Display format
inputFormats={['dd/MM/yyyy']} // What users can type
placeholder="Type: DD/MM/YYYY (converts to YYYY-MM-DD)"
/>User types: 19/03/2026 → Displays: 2026-03-19
Multiple Input Formats
Accept dates in various formats:
<DatePickerComponent
value={new Date()}
format="yyyy-MM-dd"
inputFormats={[
'dd/MM/yyyy', // 19/03/2026
'MM/dd/yyyy', // 03/19/2026
'yyyy-MM-dd', // 2026-03-19
'yyyyMMdd', // 20260319
'd MMM yyyy' // 19 Mar 2026
]}
placeholder="Enter date (multiple formats accepted)"
/>Users can type any of these and DatePicker will parse correctly:
19/03/2026(European)03/19/2026(US)2026-03-19(ISO)20260319(No separators)19 Mar 2026(Month name)
Real-World Example: Birth Date
<DatePickerComponent
id="birthDate"
value={null}
format="MM/dd/yyyy"
inputFormats={['MM/dd/yyyy', 'dd/MM/yyyy', 'yyyy-MM-dd']}
placeholder="Birth Date (MM/DD/YYYY)"
/>This accepts multiple formats, converting all to US format for consistency.
Automatic Format Conversion
When inputFormats is defined and user types a date:
1. User types a date matching one of the inputFormats 2. On blur or Enter key press, DatePicker parses the input 3. Parsed date converts to the format display format 4. Input field updates to show formatted date
Example flow:
User input: "19-03-2026"
Matches inputFormat: "dd-MM-yyyy" ✓
Parses to: Date(2026, 2, 19)
Converts to format: "yyyy-MM-dd"
Display: "2026-03-19"When Parsing Fails
If input doesn't match any inputFormats:
<DatePickerComponent
value={new Date()}
format="yyyy-MM-dd"
inputFormats={['dd/MM/yyyy']}
placeholder="Enter as DD/MM/YYYY"
/>User types: invalid-date → Field shows error styling (red border), value remains unchanged
Culture-Based Formatting
Different cultures use different date formats. Syncfusion auto-applies culture defaults:
English (en-US)
<DatePickerComponent locale="en" /> // M/d/yyyy (3/19/2026)German (de)
<DatePickerComponent locale="de" /> // d.M.yyyy (19.3.2026)French (fr)
<DatePickerComponent locale="fr" /> // dd/MM/yyyy (19/03/2026)British English (en-GB)
<DatePickerComponent locale="en-GB" /> // dd/MM/yyyy (19/03/2026)Format and Input Formats Together
Combine format and inputFormats for maximum flexibility:
<DatePickerComponent
id="datepicker"
value={new Date()}
format="dd MMM yyyy" // Display: 19 Mar 2026
inputFormats={[
'dd/MM/yyyy', // User can type: 19/03/2026
'MM/dd/yyyy', // User can type: 03/19/2026
'yyyy-MM-dd', // User can type: 2026-03-19
'dd MMM yyyy' // User can type: 19 Mar 2026
]}
placeholder="Enter date: 19/03/2026 or 03/19/2026 or 2026-03-19"
/>Result:
- Input accepts 4 different formats
- All convert to "19 Mar 2026" format for display
- Provides best user experience: flexible input, consistent display
Complete Application Example
import React, { useState } from 'react';
import { DatePickerComponent } from '@syncfusion/ej2-react-calendars';
export default function App() {
const [date, setDate] = useState(new Date());
const [formattedString, setFormattedString] = useState('');
const handleChange = (e) => {
setDate(e.value);
// Get the formatted date string
if (e.value) {
const day = String(e.value.getDate()).padStart(2, '0');
const month = String(e.value.getMonth() + 1).padStart(2, '0');
const year = e.value.getFullYear();
setFormattedString(`${day}/${month}/${year}`);
}
};
return (
<div style={{ padding: '20px' }}>
<h3>Date Format Example</h3>
<DatePickerComponent
id="datepicker"
value={date}
change={handleChange}
format="dddd, MMMM d, yyyy"
inputFormats={['dd/MM/yyyy', 'MM/dd/yyyy', 'yyyy-MM-dd']}
placeholder="Enter date (dd/MM/yyyy, MM/dd/yyyy, or YYYY-MM-DD)"
/>
<p>Formatted Display: {formattedString}</p>
<p>Underlying Date Object: {date?.toString()}</p>
</div>
);
}