Deck
Browse the catalog

Build / Molecules

EventCalendar

  • molecule

A month calendar surfacing which days carry an event, for an events index. Only marked days are selectable — the grid is a filter, not a general-purpose date picker, so a person can't pick their way into an empty day.

import { EventCalendar } from '@meetcard/deck'

Example

<EventCalendar month="2026-07" onMonthChange={setMonth} value={selected} onChange={setSelected} today="2026-07-21" markedDates={[ { date: '2026-07-21', status: 'past' }, { date: '2026-08-04', status: 'upcoming' }, ]} />

Stories

Every preview below is the real component, rendered in this page from the same story Storybook runs. The width control resizes each preview's container; CSS media queries still follow the browser window, so breakpoint behaviour is worth checking in Storybook.

Default
Source
const Default = args => {
    const [month, setMonth] = useState(args.month)
    const [value, setValue] = useState(args.value)

    return (
        <EventCalendar
            today="2026-07-21"
            markedDates={[
              { date: '2026-07-21', status: 'past' },
              { date: '2026-08-04', status: 'upcoming' },
            ]}
            month={month}
            onMonthChange={setMonth}
            value={value}
            onChange={setValue} />
    );
};

A date chosen, which the list below the calendar filters to.

Selected
Source
const Selected = args => {
    const [month, setMonth] = useState(args.month)
    const [value, setValue] = useState(args.value)

    return (
        <EventCalendar
            today="2026-07-21"
            markedDates={[
              { date: '2026-07-21', status: 'past' },
              { date: '2026-08-04', status: 'upcoming' },
            ]}
            month={month}
            onMonthChange={setMonth}
            value={value}
            onChange={setValue} />
    );
};

Days without an event are disabled — the grid filters, it doesn't invite a dead click.

No Events This Month
Source
const NoEventsThisMonth = args => {
    const [month, setMonth] = useState(args.month)
    const [value, setValue] = useState(args.value)

    return (
        <EventCalendar
            today="2026-07-21"
            markedDates={[{ date: '2026-08-04', status: 'upcoming' }]}
            month={month}
            onMonthChange={setMonth}
            value={value}
            onChange={setValue} />
    );
};

A busy month. Past and upcoming stay distinct at a glance, and in the label.

Many Marked Dates
Source
const ManyMarkedDates = args => {
    const [month, setMonth] = useState(args.month)
    const [value, setValue] = useState(args.value)

    return (
        <EventCalendar
            today="2026-07-21"
            markedDates={[
              { date: '2026-07-02', status: 'past' },
              { date: '2026-07-08', status: 'past' },
              { date: '2026-07-14', status: 'past' },
              { date: '2026-07-21', status: 'past' },
              { date: '2026-07-24', status: 'upcoming' },
              { date: '2026-07-28', status: 'upcoming' },
            ]}
            month={month}
            onMonthChange={setMonth}
            value={value}
            onChange={setValue} />
    );
};

Props

PropTypeDefaultDescription
markedDatesrequiredEventCalendarMarkedDate[]—Days carrying an event. Only these are selectable.
monthrequiredstring—ISO month, YYYY-MM, the calendar currently displays.
classNamestring——
localestring——
onChange(date: string) => void——
onMonthChange(month: string) => void——
todaystring—ISO date rendered with a "Today" marker.
valuestring—ISO date of the selected day.

Open in Storybookfor controls, the a11y panel and the interaction runner.