Building a Datepicker in React

Okay, here’s a comprehensive article on building a datepicker component in React, aiming for approximately 5000 words. This will cover a wide range of topics, from basic setup to advanced features and considerations.

Building a Datepicker in React: A Comprehensive Guide

Datepickers are a ubiquitous element in modern web applications, providing a user-friendly way to select dates for forms, scheduling tools, data analysis interfaces, and countless other scenarios. While numerous pre-built datepicker libraries exist for React, building one from scratch offers invaluable learning opportunities and allows for complete customization to fit specific project needs. This guide will walk you through the process of creating a flexible, accessible, and robust datepicker component in React, covering everything from basic functionality to advanced features.

I. Project Setup and Basic Structure

Before diving into the component itself, we need to set up a basic React project. We’ll use create-react-app for a quick and easy setup, but you can adapt these steps to your preferred build environment.

  1. Create the Project:

    bash
    npx create-react-app react-datepicker-tutorial
    cd react-datepicker-tutorial

  2. Install Dependencies (Optional – but recommended):

    We’ll use a few helpful libraries to simplify date manipulation and handling keyboard events:

    • date-fns: A modern, lightweight, and modular date utility library. It’s a great alternative to Moment.js, offering better tree-shaking and smaller bundle sizes.
    • react-use: A collection of useful React hooks, including useClickAway which is helpful for closing the datepicker when clicking outside. (We’ll use this later)

    bash
    npm install date-fns react-use

  3. Component Structure:

    Create a new file named Datepicker.js (or Datepicker.jsx) within your src directory. We’ll start with a basic functional component structure:

    “`javascript
    // src/Datepicker.js
    import React, { useState } from ‘react’;
    import { format, startOfMonth, endOfMonth, eachDayOfInterval, isSameDay, isSameMonth } from ‘date-fns’;

    function Datepicker() {
    const [selectedDate, setSelectedDate] = useState(new Date()); // Start with today’s date
    const [currentMonth, setCurrentMonth] = useState(new Date()); // Track the currently displayed month

    return (
        <div className="datepicker">
            {/* Datepicker UI will go here */}
            <h1>{format(currentMonth, 'MMMM yyyy')}</h1> {/* Display the current month and year */}
            <button onClick={() => setCurrentMonth(dateFns.subMonths(currentMonth, 1))}>Previous</button>
            <button onClick={() => setCurrentMonth(dateFns.addMonths(currentMonth, 1))}>Next</button>
        </div>
    );
    

    }

    export default Datepicker;
    “`
    We have added next and previous buttons to go to the previous and next months.

  4. Import the Component:
    “`javascript
    // src/App.js
    import React from ‘react’;
    import Datepicker from ‘./Datepicker’;
    import ‘./App.css’;

    function App() {
    return (

    );
    }

    export default App;

    “`

  5. Basic Styling (Optional):

    Create a Datepicker.css file (or use styled-components, CSS modules, etc.) to add some basic styling:

    css
    /* src/Datepicker.css */
    .datepicker {
    border: 1px solid #ccc;
    border-radius: 4px;
    padding: 10px;
    font-family: sans-serif;
    }

    Import this css file into the Datepicker component.

    javascript
    // src/Datepicker.js
    import './Datepicker.css';

II. Building the Calendar Grid

The core of the datepicker is the calendar grid that displays the days of the month. We’ll use date-fns to generate the necessary data.

  1. Generate Days of the Month:

    Inside the Datepicker component, add a function to generate an array of dates for the current month:

    “`javascript
    // src/Datepicker.js
    function Datepicker() {
    // … (previous state and imports)

    const renderDays = () => {
        const startDate = startOfMonth(currentMonth);
        const endDate = endOfMonth(currentMonth);
        const days = eachDayOfInterval({ start: startDate, end: endDate });
    
        return days.map((day) => (
            <div
                key={day.toISOString()}
                className={`day ${isSameDay(day, selectedDate) ? 'selected' : ''}`}
                onClick={() => setSelectedDate(day)}
            >
                {format(day, 'd')}
            </div>
        ));
    };
    
    return (
        <div className="datepicker">
            <h1>{format(currentMonth, 'MMMM yyyy')}</h1>
            <button onClick={() => setCurrentMonth(dateFns.subMonths(currentMonth, 1))}>Previous</button>
            <button onClick={() => setCurrentMonth(dateFns.addMonths(currentMonth, 1))}>Next</button>
            <div className="calendar-grid">
                {renderDays()}
            </div>
        </div>
    );
    

    }
    “`

    • startOfMonth(currentMonth): Gets the first day of the currentMonth.
    • endOfMonth(currentMonth): Gets the last day of the currentMonth.
    • eachDayOfInterval({ start: startDate, end: endDate }): Returns an array of dates between the startDate and endDate (inclusive).
    • format(day, 'd'): Formats the date to display only the day of the month (e.g., “1”, “15”, “31”).
    • isSameDay(day, selectedDate): Checks if the current day is the same as the selectedDate. We use this to apply a “selected” class.
    • We added basic styling to the calendar grid, and a selected class to the div representing the currently selected date.
  2. Add Weekday Headers:

    Let’s add headers for the days of the week (Sun, Mon, Tue, etc.):

    “`javascript
    // src/Datepicker.js
    function Datepicker() {
    // … (previous state, imports, and renderDays)

    const renderWeekdays = () => {
        const weekdays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
        return weekdays.map((day) => (
            <div key={day} className="weekday">
                {day}
            </div>
        ));
    };
    
    return (
        <div className="datepicker">
          {/*... previous and next month buttons */}
            <div className="calendar-grid">
                {renderWeekdays()}
                {renderDays()}
            </div>
        </div>
    );
    

    }

    “`

  3. Styling the Calendar Grid:

    Update your Datepicker.css to style the grid and weekdays:

    “`css
    / src/Datepicker.css /
    .calendar-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr); / 7 columns for the days of the week /
    gap: 2px; / Add some spacing between days /
    }

    .weekday {
    text-align: center;
    font-weight: bold;
    padding: 5px;
    border-bottom: 1px solid #ccc;
    }

    .day {
    text-align: center;
    padding: 5px;
    cursor: pointer; / Make days clickable /
    border-radius: 4px;
    }

    .day:hover {
    background-color: #eee;
    }

    .day.selected {
    background-color: #007bff; / Highlight the selected day /
    color: white;
    }
    “`

III. Handling Days from Previous/Next Months

Currently, the calendar only shows days from the currentMonth. To make the calendar look complete, we need to include days from the previous and next months to fill out the first and last weeks.

  1. Calculate Leading and Trailing Days:

    We’ll modify the renderDays function to include these days:

    “`javascript
    // src/Datepicker.js
    import { startOfWeek, endOfWeek, addDays } from ‘date-fns’;
    //…
    const renderDays = () => {
    const startDate = startOfWeek(startOfMonth(currentMonth)); // Start of the week of the first day
    const endDate = endOfWeek(endOfMonth(currentMonth)); // End of the week of the last day
    const days = eachDayOfInterval({ start: startDate, end: endDate });

        return days.map((day) => (
            <div
                key={day.toISOString()}
                className={`day
                    ${isSameDay(day, selectedDate) ? 'selected' : ''}
                    ${isSameMonth(day, currentMonth) ? '' : 'other-month'}
                `}
                onClick={() => setSelectedDate(day)}
            >
                {format(day, 'd')}
            </div>
        ));
    };
    

    “`

    • startOfWeek(startOfMonth(currentMonth)): Gets the first day of the week containing the first day of the month. This will often be a day in the previous month.
    • endOfWeek(endOfMonth(currentMonth)): Gets the last day of the week containing the last day of the month. This will often be a day in the next month.
    • isSameMonth(day, currentMonth): Checks if the day belongs to the currentMonth. We use this to apply an other-month class.
  2. Styling Other Month Days:

    Add a style for days that are not in the current month:

    css
    /* src/Datepicker.css */
    .day.other-month {
    color: #999; /* Lighter color for days outside the current month */
    }

IV. Input Field and Datepicker Visibility

Now, let’s add an input field that will display the selected date and control the visibility of the calendar.

  1. Add Input Field and State:

    “`javascript
    // src/Datepicker.js
    import React, { useState, useRef } from ‘react’; // Import useRef
    import { useClickAway } from ‘react-use’; // Import useClickAway
    //…

    function Datepicker() {
    const [selectedDate, setSelectedDate] = useState(null); // Initialize as null
    const [currentMonth, setCurrentMonth] = useState(new Date());
    const [isOpen, setIsOpen] = useState(false); // Control calendar visibility
    const datepickerRef = useRef(null); // Ref for the entire datepicker

    useClickAway(datepickerRef, () => {
        setIsOpen(false); // Close the calendar when clicking outside
    });
    //... (renderWeekdays and renderDays functions)
    return (
      <div className="datepicker" ref={datepickerRef}>
          <input
              type="text"
              value={selectedDate ? format(selectedDate, 'yyyy-MM-dd') : ''}
              onClick={() => setIsOpen(!isOpen)}
              readOnly // Prevent manual text input
              placeholder="Select a date"
          />
          {isOpen && (
              <div className="calendar">
                {/* Calendar content (header, weekdays, days) */}
                  <h1>{format(currentMonth, 'MMMM yyyy')}</h1>
                  <button onClick={() => setCurrentMonth(dateFns.subMonths(currentMonth, 1))}>Previous</button>
                  <button onClick={() => setCurrentMonth(dateFns.addMonths(currentMonth, 1))}>Next</button>
                  <div className="calendar-grid">
                      {renderWeekdays()}
                      {renderDays()}
                  </div>
              </div>
          )}
      </div>
    

    );
    }

    “`

    • isOpen: A state variable to control the visibility of the calendar.
    • datepickerRef: A ref attached to the outermost div of the datepicker. We use this with useClickAway.
    • useClickAway(datepickerRef, () => setIsOpen(false)): This hook from react-use closes the calendar when the user clicks anywhere outside the datepicker element.
    • value={selectedDate ? format(selectedDate, 'yyyy-MM-dd') : ''}: Displays the selected date in the input field, formatted as “yyyy-MM-dd”. If no date, display nothing.
    • onClick={() => setIsOpen(!isOpen)}: Toggles the isOpen state when the input field is clicked.
    • readOnly: Prevents the user from typing directly into the input field.
    • placeholder: Adds placeholder text to guide the user.
    • We wrapped the calendar content (header, weekdays, days) in a div with the class calendar and conditionally render it based on isOpen.
  2. Styling the Input and Calendar Container:

    “`css
    / src/Datepicker.css /
    .datepicker {
    position: relative; / Make the datepicker a positioning context /
    display: inline-block; / Allow the datepicker to fit its content /
    / … (previous styles) /
    }

    .calendar {
    position: absolute; / Position the calendar relative to the datepicker /
    top: 100%; / Position it below the input field /
    left: 0;
    background-color: white;
    border: 1px solid #ccc;
    border-radius: 4px;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.15); / Add a subtle shadow /
    z-index: 10; / Ensure the calendar appears above other elements /
    }

    input[type=”text”] {
    padding: 8px;
    border: 1px solid #ccc;
    border-radius: 4px;
    width: 150px; / Adjust width as needed /
    }
    ``
    *
    position: relative;on.datepicker: This is crucial for absolutely positioning the calendar.
    *
    position: absolute;on.calendar: Positions the calendar relative to the.datepickercontainer.
    *
    top: 100%;: Places the calendar just below the input field.
    *
    z-index: 10;`: Ensures the calendar appears on top of other elements on the page.

  3. Update setSelectedDate and Close Calendar:
    Modify onClick handler in renderDays to set isOpen to false.

    “`javascript
    //src/Datepicker.js
    const renderDays = () => {
    // … (previous code)

        return days.map((day) => (
            <div
                // ... (previous props)
                onClick={() => {
                    setSelectedDate(day);
                    setIsOpen(false); // Close the calendar after selection
                }}
            >
                {format(day, 'd')}
            </div>
        ));
    };
    

    “`

V. Keyboard Navigation and Accessibility

A good datepicker should be fully navigable with the keyboard and accessible to users with assistive technologies.

  1. Keyboard Event Handling:

    We’ll add event listeners to the calendar container to handle arrow keys, Enter, and Escape.

    “`javascript
    // src/Datepicker.js
    import React, { useState, useRef, useEffect } from ‘react’; // Import useEffect
    //…

    function Datepicker() {
    // … (previous state and refs)

    useEffect(() => {
        const handleKeyDown = (event) => {
            if (!isOpen) return; // Only handle keys if the calendar is open
    
            switch (event.key) {
                case 'ArrowLeft':
                    setSelectedDate((prevDate) =>
                        prevDate ? addDays(prevDate, -1) : new Date()
                    );
                    break;
                case 'ArrowRight':
                    setSelectedDate((prevDate) =>
                        prevDate ? addDays(prevDate, 1) : new Date()
                    );
                    break;
                case 'ArrowUp':
                    setSelectedDate((prevDate) =>
                        prevDate ? addDays(prevDate, -7) : new Date()
                    );
                    break;
                case 'ArrowDown':
                    setSelectedDate((prevDate) =>
                        prevDate ? addDays(prevDate, 7) : new Date()
                    );
                    break;
                case 'Enter':
                     if (selectedDate && isSameMonth(selectedDate, currentMonth)) { //select only if selected date is in the current month
                        setIsOpen(false);
                    }
                    break;
                case 'Escape':
                    setIsOpen(false);
                    break;
                default:
                    break;
            }
        };
    
        if (isOpen) {
            document.addEventListener('keydown', handleKeyDown);
        }
    
        return () => {
            document.removeEventListener('keydown', handleKeyDown);
        };
    }, [isOpen, selectedDate, currentMonth]); // Dependencies for useEffect
    
    // ... (renderWeekdays, renderDays, and return statement)
    

    }
    “`

    • useEffect: We use useEffect to add and remove the keydown event listener. This ensures that the listener is only active when the calendar is open.
    • handleKeyDown: This function handles the key presses.
    • ArrowLeft, ArrowRight, ArrowUp, ArrowDown: Move the selected date by one day or one week.
    • Enter: Selects the currently focused date (if it is in current month) and closes the calendar.
    • Escape: Closes the calendar.
    • The return function inside useEffect is a cleanup function that removes the event listener when the component unmounts or when isOpen changes. This prevents memory leaks.
  2. Focus Management:

    We need to ensure that focus is properly managed when the calendar opens and closes.

    ``javascript
    // src/Datepicker.js
    //...
    const inputRef = useRef(null); // Ref for the input field
    //...
    useEffect(() => {
    if (isOpen) {
    // Focus the first day of the month when the calendar opens.
    // Find a better way to focus a specific day element if needed.
    const firstDayOfMonth = startOfMonth(currentMonth);
    if(isSameMonth(selectedDate, currentMonth)){
    const selectedDayElement = document.querySelector(
    .day[data-date=”${selectedDate.toISOString()}”]);
    selectedDayElement?.focus();
    } else{
    const firstDayElement = document.querySelector(
    .day[data-date=”${firstDayOfMonth.toISOString()}”]`);
    firstDayElement?.focus();
    }

       }
    

    }, [isOpen, currentMonth, selectedDate]);
    //…
    //In renderDays

    day ${isSameDay(day, selectedDate) ? ‘selected’ : ”}
    ${isSameMonth(day, currentMonth) ? ” : ‘other-month’}}
    onClick={() => {
    setSelectedDate(day);
    setIsOpen(false);
    inputRef.current.focus(); // Focus the input field after selection
    }}
    tabIndex={isSameMonth(day, currentMonth) ? 0 : -1} // Make days in the current month focusable
    data-date={day.toISOString()}

    {format(day, 'd')}
    

    //…
    // In return() method, update input field with ref.
    setIsOpen(!isOpen)}
    readOnly
    placeholder=”Select a date”
    ref={inputRef} // Attach the ref to the input field
    />
    “`

    • inputRef: A ref for the input field.
    • Inside renderDays, added tabIndex={isSameMonth(day, currentMonth) ? 0 : -1}: Makes the days in the current month focusable using the Tab key, while days from other months are not.
    • Inside renderDays, inputRef.current.focus(): Focuses the input field after a date is selected.
    • Added data-date={day.toISOString()} to the day elements for easier selection in the useEffect.
    • In the useEffect which handles calendar opening: We are manually managing the focus of day elements when calendar is opened. We use querySelector to find the appropriate day element and call .focus() on it. If a date is already selected, and it is in the currently displayed month, we focus it. Otherwise, we focus the first day of the current month.
  3. ARIA Attributes (Accessibility):

    Add ARIA attributes to improve accessibility for screen readers.

    “`javascript
    // src/Datepicker.js

    function Datepicker() {
    // … (previous code)

    return (
        <div className="datepicker" ref={datepickerRef} role="application">
            <input
                // ... (previous props)
                aria-label="Select a date"
                aria-haspopup="dialog" // Indicates that the input opens a dialog
                aria-expanded={isOpen}  // Indicates whether the calendar is open
            />
            {isOpen && (
                <div
                    className="calendar"
                    role="dialog" // The calendar acts as a dialog
                    aria-label="Calendar"
                    aria-modal="true" // Indicates that the calendar is modal (blocks interaction with the rest of the page)
                >
                    <h1>{format(currentMonth, 'MMMM yyyy')}</h1>
                    {/* next and previous button */}
                    <div className="calendar-grid">
                        {renderWeekdays()}
                        {renderDays()}
                    </div>
                </div>
            )}
        </div>
    );
    

    }
    “`

    • role="application": On the main datepicker container. This tells assistive technologies that this is a custom widget.
    • aria-label="Select a date": Provides a label for the input field.
    • aria-haspopup="dialog": Indicates that the input field opens a dialog (the calendar).
    • aria-expanded={isOpen}: Indicates whether the calendar is currently open or closed.
    • role="dialog": On the calendar container. This indicates that the calendar is a dialog window.
    • aria-label="Calendar": Provides a label for the calendar.
    • aria-modal="true": Indicates that the calendar is modal, meaning it prevents interaction with the rest of the page until it’s closed.
    • Inside renderDays add aria-selected and aria-label:

    javascript
    <div
    //...other props
    aria-selected={isSameDay(day, selectedDate)} // Indicates whether the day is selected
    aria-label={format(day, 'MMMM dd, yyyy')} // Provides a full date label for screen readers
    >
    {format(day, 'd')}
    </div>

VI. Advanced Features (Optional)

Let’s explore some more advanced features you might want to add to your datepicker.

  1. Date Range Selection:

    To support selecting a range of dates (start and end), you’ll need to modify the state and logic:

    “`javascript
    // src/Datepicker.js
    import { isWithinInterval } from ‘date-fns’; // Import isWithinInterval

    function Datepicker() {
    const [startDate, setStartDate] = useState(null);
    const [endDate, setEndDate] = useState(null);
    const [hoveredDate, setHoveredDate] = useState(null); // For highlighting the range on hover
    //…

    const renderDays = () => {
            const startDateOfWeek = startOfWeek(startOfMonth(currentMonth));
            const endDateOfWeek = endOfWeek(endOfMonth(currentMonth));
            const days = eachDayOfInterval({ start: startDateOfWeek, end: endDateOfWeek });
    
            return days.map((day) => {
                const isSelected = (startDate && isSameDay(day, startDate)) || (endDate && isSameDay(day, endDate));
                const isInRange = startDate && endDate && isWithinInterval(day, { start: startDate, end: endDate });
                const isHoveredRange = startDate && !endDate && hoveredDate && isWithinInterval(day, {start: startDate, end: hoveredDate});
                return (
                    <div
                        key={day.toISOString()}
                        className={`day
                            ${isSelected ? 'selected' : ''}
                            ${isInRange || isHoveredRange? 'in-range' : ''}
                            ${isSameMonth(day, currentMonth) ? '' : 'other-month'}
                        `}
                        onClick={() => {
                            if (!startDate) {
                                setStartDate(day);
                            } else if (!endDate) {
                                setEndDate(day);
                                 setIsOpen(false);
                                inputRef.current.focus();
                            } else {
                                setStartDate(day);
                                setEndDate(null);
                            }
                        }}
                        onMouseEnter={() => setHoveredDate(day)}
                        onMouseLeave={() => setHoveredDate(null)}
                        tabIndex={isSameMonth(day, currentMonth) ? 0 : -1}
                        data-date={day.toISOString()}
                        aria-selected={isSelected}
                        aria-label={format(day, 'MMMM dd, yyyy')}
                    >
                        {format(day, 'd')}
                    </div>
                );
            });
        };
    
    return (
        <div className="datepicker" ref={datepickerRef} role="application">
          <input
            type="text"
            value={
                startDate && endDate
                    ? `${format(startDate, 'yyyy-MM-dd')} - ${format(endDate, 'yyyy-MM-dd')}`
                    : startDate ? `${format(startDate, 'yyyy-MM-dd')} -` : ''
            }
            onClick={() => setIsOpen(!isOpen)}
            readOnly
            placeholder="Select a date range"
            ref={inputRef}
            aria-label="Select a date range"
            aria-haspopup="dialog"
            aria-expanded={isOpen}
        />
        {/* ... rest of the component ... */}
        </div>
    );
    

    }
    “`

    • startDate, endDate: State variables to store the start and end dates of the range.
    • hoveredDate: A state variable to track the date currently being hovered over, used to visually highlight the range during selection.
    • isWithinInterval(day, { start: startDate, end: endDate }): Checks if a day falls within the selected range.
    • Modified onClick: Logic to handle setting the startDate and endDate. If start date is not selected, select it. If end date is not selected, select it, close calendar, and refocus to input. If both dates are selected, restart selection by setting selected date to the clicked date, and end date to null.
    • onMouseEnter, onMouseLeave: Used to update hoveredDate for range highlighting.
    • isInRange: Added to the className, this will be defined in the css.
    • Update input field value to reflect range selection.

    Add styling for the range:

    css
    /* src/Datepicker.css */
    .day.in-range {
    background-color: #b3d9ff; /* Light blue background for days in the range */
    }

  2. Disabling Dates:

    You might want to disable certain dates (e.g., past dates, weekends, specific holidays).

    “`javascript
    // src/Datepicker.js
    function Datepicker({ disabledDates = [] }) { // Receive disabledDates as a prop
    // …

    const renderDays = () => {
    // … (previous code)

    return days.map((day) => {
      const isDisabled = disabledDates.some((disabledDate) => isSameDay(day, disabledDate));
    
      return (
        <div
          key={day.toISOString()}
          className={`day
            ${isSameDay(day, selectedDate) ? 'selected' : ''}
            ${isSameMonth(day, currentMonth) ? '' : 'other-month'}
            ${isDisabled ? 'disabled' : ''} 
          `}
          onClick={() => {
            if (!isDisabled) {
              setSelectedDate(day);
              setIsOpen(false);
              inputRef.current.focus();
            }
          }}
          tabIndex={isSameMonth(day, currentMonth) && !isDisabled ? 0 : -1}
          data-date={day.toISOString()}
          aria-selected={isSameDay(day, selectedDate)}
          aria-label={format(day, 'MMMM dd, yyyy')}
          aria-disabled={isDisabled} // Add aria-disabled attribute
        >
          {format(day, 'd')}
        </div>
      );
    });
    

    };
    // …
    }
    “`

    • disabledDates: An array of dates that should be disabled, passed as a prop.
    • isDisabled: Checks if the current day is in the disabledDates array.
    • Added disabled to className.
    • onClick: Only allows selection if the date is not disabled.
    • aria-disabled={isDisabled}: Adds the aria-disabled attribute for accessibility.
    • Update tabIndex to also depend on isDisabled.
      Add a style for disabled days:

    “`css
    / src/Datepicker.css /
    .day.disabled {
    color: #ccc; / Gray out disabled days /
    cursor: not-allowed; / Change cursor to indicate unclickable /
    background-color: #f9f9f9;
    }

    .day.disabled:hover {
    background-color: #f9f9f9; / Prevent hover effect on disabled days /
    }
    “`

    To use this, you would pass an array of dates to the Datepicker component:

    javascript
    // src/App.js
    import { parseISO } from 'date-fns';
    //...
    <Datepicker disabledDates={[parseISO('2024-03-08'), parseISO('2024-03-15')]} />

  3. Customizable Week Start:

    By default date-fns considers Sunday as the start of the week. You might want to allow users to start the week on a different day (e.g., Monday).
    “`javascript
    // src/Datepicker.js
    function Datepicker({ weekStartsOn = 0 }) { // Default to Sunday (0)
    // … (previous code)

    const renderWeekdays = () => {
        const weekdays = [];
        let day = startOfWeek(new Date(), { weekStartsOn }); //start the week based on the provided day
    
        for (let i = 0; i < 7; i++) {
            weekdays.push(format(day, 'EEE')); // Use 'EEE' for short weekday names (Sun, Mon, etc.)
            day = addDays(day, 1);
        }
    
        return weekdays.map((day) => (
            <div key={day} className="weekday">
                {day}
            </div>
        ));
    };
    
    const renderDays = () => {
       const startDate = startOfWeek(startOfMonth(currentMonth), { weekStartsOn });
       const endDate = endOfWeek(endOfMonth(currentMonth), { weekStartsOn });
       //....Rest of renderDays
    }
    

    }

    ``
    *
    weekStartsOn: Added to the parameters of Datepicker component. This prop specifies the day of the week to start on (0 for Sunday, 1 for Monday, etc.). It defaults to 0 (Sunday).
    * Updated
    renderDaysto takeweekStartsOninto account while getting the start and end dates.
    * Updated
    renderWeekdaysmethod to show the correct weekdays, starting fromweekStartsOn`.

  4. Localization:

    To support different languages and date formats, you can use date-fns‘s locale support.

    “`javascript
    // src/Datepicker.js
    import { format, startOfMonth, endOfMonth, eachDayOfInterval, isSameDay, isSameMonth, startOfWeek, endOfWeek, addDays } from ‘date-fns’;
    import { enUS, fr, es } from ‘date-fns/locale’; // Import desired locales

    function Datepicker({ locale = enUS }) { // Default to enUS
    //… (previous code, including weekStartsOn change.)
    const renderWeekdays = () => {
    const weekdays = [];
    let day = startOfWeek(new Date(), { weekStartsOn, locale: locale });

        for (let i = 0; i < 7; i++) {
            weekdays.push(format(day, 'EEE', { locale: locale }));
            day = addDays(day, 1);
        }
    
        return weekdays.map((day) => (
            <div key={day} className="weekday">
                {day}
            </div>
        ));
    };
    

    const renderDays = () => {
    const startDate = startOfWeek(startOfMonth(currentMonth), { weekStartsOn: weekStartsOn, locale: locale});
    const endDate = endOfWeek(endOfMonth(currentMonth), { weekStartsOn: weekStartsOn, locale: locale });
    const days = eachDayOfInterval({ start: startDate, end: endDate });

    return days.map((day) => {
      //... (previous code in renderDays)
    
      return (
        <div
          // ... (previous props)
    
          aria-label={format(day, 'PPP', { locale: locale })} // Use 'PPP' for localized long date format
    
        >
          {format(day, 'd')}
        </div>
      );
    });
    

    };

    return (

    setIsOpen(!isOpen)}
    readOnly
    placeholder=”Select a date”
    ref={inputRef}
    aria-label=”Select a date”
    aria-haspopup=”dialog”
    aria-expanded={isOpen}
    />
    {/ … rest of the component … /}

    );

    }
    “`

    • locale: A prop to specify the locale to use (e.g., enUS, fr, es). Defaults to enUS.
    • Import

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top