Tips & Tricks

How to Build a Calendar Component with Claude Code

Learn how to build a calendar component using Claude Code. Includes practical code examples and step-by-step guidance.

The Demand for Calendar Components

Calendar UIs are needed in many applications: booking systems, schedule management, date pickers, and more. You can rely on a third-party library, but if you want full control over the design and features, rolling your own is the right move. With Claude Code, you can build a flexible calendar component quickly.

Basic Month-View Calendar

> Create a month-view calendar component using date-fns.
> Support month switching and date selection.
import { useState } from 'react';
import {
  startOfMonth, endOfMonth, startOfWeek, endOfWeek,
  eachDayOfInterval, format, addMonths, subMonths, isSameMonth, isSameDay, isToday,
} from 'date-fns';
import { enUS } from 'date-fns/locale';

interface CalendarProps {
  selected?: Date;
  onSelect: (date: Date) => void;
  events?: { date: Date; title: string }[];
}

function Calendar({ selected, onSelect, events = [] }: CalendarProps) {
  const [currentMonth, setCurrentMonth] = useState(new Date());

  const monthStart = startOfMonth(currentMonth);
  const monthEnd = endOfMonth(currentMonth);
  const calendarStart = startOfWeek(monthStart, { locale: enUS });
  const calendarEnd = endOfWeek(monthEnd, { locale: enUS });
  const days = eachDayOfInterval({ start: calendarStart, end: calendarEnd });

  const weekDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];

  const getEventsForDay = (day: Date) =>
    events.filter((e) => isSameDay(e.date, day));

  return (
    <div className="w-full max-w-md mx-auto" role="application" aria-label="calendar">
      <div className="flex items-center justify-between mb-4">
        <button onClick={() => setCurrentMonth(subMonths(currentMonth, 1))} aria-label="Previous month">

        </button>
        <h2 className="text-lg font-bold" aria-live="polite">
          {format(currentMonth, 'MMMM yyyy', { locale: enUS })}
        </h2>
        <button onClick={() => setCurrentMonth(addMonths(currentMonth, 1))} aria-label="Next month">

        </button>
      </div>

      <div className="grid grid-cols-7 gap-px" role="grid">
        {weekDays.map((day) => (
          <div key={day} className="text-center text-sm font-medium text-gray-500 py-2" role="columnheader">
            {day}
          </div>
        ))}

        {days.map((day) => {
          const dayEvents = getEventsForDay(day);
          const isSelected = selected && isSameDay(day, selected);
          const inMonth = isSameMonth(day, currentMonth);

          return (
            <button
              key={day.toISOString()}
              onClick={() => onSelect(day)}
              role="gridcell"
              aria-selected={isSelected}
              aria-label={`${format(day, 'MMMM d', { locale: enUS })}${dayEvents.length > 0 ? ` ${dayEvents.length} events` : ''}`}
              className={`p-2 text-center rounded-lg relative ${
                !inMonth ? 'text-gray-300' :
                isSelected ? 'bg-blue-600 text-white' :
                isToday(day) ? 'bg-blue-100 font-bold' :
                'hover:bg-gray-100'
              }`}
            >
              {format(day, 'd')}
              {dayEvents.length > 0 && (
                <span className="absolute bottom-1 left-1/2 -translate-x-1/2 w-1 h-1 rounded-full bg-blue-500" />
              )}
            </button>
          );
        })}
      </div>
    </div>
  );
}

Date Range Selection

> Add support for date range selection, like check-in / check-out.
function useDateRange() {
  const [range, setRange] = useState<{ start: Date | null; end: Date | null }>({
    start: null,
    end: null,
  });

  const handleSelect = (date: Date) => {
    if (!range.start || (range.start && range.end)) {
      setRange({ start: date, end: null });
    } else if (date < range.start) {
      setRange({ start: date, end: range.start });
    } else {
      setRange({ start: range.start, end: date });
    }
  };

  const isInRange = (date: Date) => {
    if (!range.start || !range.end) return false;
    return date >= range.start && date <= range.end;
  };

  return { range, handleSelect, isInRange };
}

Calendar With Event Display

function EventCalendar({ events }: { events: CalendarEvent[] }) {
  const [selectedDate, setSelectedDate] = useState<Date>(new Date());
  const dayEvents = events.filter((e) => isSameDay(e.date, selectedDate));

  return (
    <div className="flex gap-4">
      <Calendar selected={selectedDate} onSelect={setSelectedDate} events={events} />
      <div className="flex-1">
        <h3 className="font-bold mb-2">
          Events for {format(selectedDate, 'EEEE, MMMM d', { locale: enUS })}
        </h3>
        {dayEvents.length === 0 ? (
          <p className="text-gray-500">No events</p>
        ) : (
          <ul className="space-y-2">
            {dayEvents.map((event) => (
              <li key={event.id} className="p-3 rounded-lg border">
                <span className="font-medium">{event.title}</span>
                <span className="text-sm text-gray-500 ml-2">
                  {format(event.date, 'HH:mm')}
                </span>
              </li>
            ))}
          </ul>
        )}
      </div>
    </div>
  );
}

Summary

With Claude Code, you can flexibly build calendar components that fit your use case, including month view, date range selection, and event display. For pairing with tables, see table component, and for responsive behavior, see responsive design.

For more on date-fns, see the official date-fns site.

#Claude Code #calendar #React #UI #date-fns

Level up your Claude Code workflow

50 battle-tested prompt templates you can copy-paste into Claude Code right now.

Free

Free PDF: Claude Code Cheatsheet in 5 Minutes

Key commands, shortcuts, and prompt examples on a single printable page.

Download PDF
M

About the Author

Masa

Engineer obsessed with Claude Code. Runs claudecode-lab.com, a 10-language tech media with 2,000+ pages.