Propel

Calendar

A keyboard- and screen-reader-accessible date picker for single-date and range selection.

Show code
import { Calendar } from "@makeplane/propel/components/calendar";
import * as React from "react";

export default function BasicDemo() {
  const [selected, setSelected] = React.useState<Date | undefined>(new Date(2025, 0, 24));

  return (
    <Calendar
      mode="single"
      defaultMonth={new Date(2025, 0, 1)}
      selected={selected}
      onSelect={setSelected}
    />
  );
}

Installation

import { Calendar } from "@makeplane/propel/components/calendar";

Usage

import { Calendar } from "@makeplane/propel/components/calendar";
import * as React from "react";

export default function BasicDemo() {
  const [selected, setSelected] = React.useState<Date | undefined>(new Date(2025, 0, 24));

  return (
    <Calendar
      mode="single"
      defaultMonth={new Date(2025, 0, 1)}
      selected={selected}
      onSelect={setSelected}
    />
  );
}

Calendar starts in the day view. Select its month or year heading to browse months and years, then choose one to return to the day grid. It draws no surface of its own — no border, shadow, fill, radius or padding — so the host owns the chrome. Put it in a PopoverContent for a date picker, or in a dialog or a card, and it takes that surface’s single border and gutters instead of adding its own.

Examples

Date picker

The grid inside a rich PopoverContent: the popover paints the border, shadow, radius and 16px gutters, and the trigger’s label follows the selection. Picking a day closes the panel.

Show code
import { Button } from "@makeplane/propel/components/button";
import { Calendar } from "@makeplane/propel/components/calendar";
import { Icon } from "@makeplane/propel/components/icon";
import { Popover, PopoverContent, PopoverTrigger } from "@makeplane/propel/components/popover";
import { CalendarOutline } from "@makeplane/propel/icons";
import * as React from "react";

const triggerDate = new Intl.DateTimeFormat("en-US", { dateStyle: "medium" });

export default function DatePickerDemo() {
  const [selected, setSelected] = React.useState<Date | undefined>(new Date(2025, 0, 24));
  const [open, setOpen] = React.useState(false);

  return (
    <Popover open={open} onOpenChange={setOpen}>
      <Button
        stretch="auto"
        variant="secondary"
        size="md"
        icon={<Icon icon={CalendarOutline} />}
        render={<PopoverTrigger />}
        label={selected ? triggerDate.format(selected) : "Pick a date"}
      />
      {/* The popover owns the surface — border, shadow, radius, gutters. The grid draws none. */}
      <PopoverContent positionMethod="fixed" aria-label="Pick a date">
        <Calendar
          mode="single"
          defaultMonth={new Date(2025, 0, 1)}
          selected={selected}
          onSelect={(day) => {
            setSelected(day);
            setOpen(false);
          }}
        />
      </PopoverContent>
    </Popover>
  );
}

Range

Show code
import { Calendar } from "@makeplane/propel/components/calendar";
import * as React from "react";

// Mirrors react-day-picker's `DateRange` shape without importing it (demos import
// only from `@makeplane/propel/components/*`).
type DateRange = { from: Date | undefined; to?: Date | undefined };

export default function RangeDemo() {
  const [range, setRange] = React.useState<DateRange | undefined>({
    from: new Date(2025, 0, 13),
    to: new Date(2025, 0, 20),
  });

  return (
    <Calendar
      mode="range"
      defaultMonth={new Date(2025, 0, 1)}
      selected={range}
      onSelect={setRange}
    />
  );
}

Disabled days

Show code
import { Calendar } from "@makeplane/propel/components/calendar";
import * as React from "react";

export default function DisabledDaysDemo() {
  const [selected, setSelected] = React.useState<Date | undefined>();

  return (
    <Calendar
      mode="single"
      defaultMonth={new Date(2025, 0, 1)}
      selected={selected}
      onSelect={setSelected}
      // Weekends (Sunday and Saturday) render muted and cannot be selected.
      disabled={{ dayOfWeek: [0, 6] }}
    />
  );
}

Today indicator

The current date is marked with an accent dot below the day number; the dot persists when today is selected. Pin it with the today prop — here it’s set to January 6 for a deterministic grid.

Show code
import { Calendar } from "@makeplane/propel/components/calendar";
import * as React from "react";

export default function TodayIndicatorDemo() {
  const [selected, setSelected] = React.useState<Date | undefined>();

  return (
    <Calendar
      mode="single"
      defaultMonth={new Date(2025, 0, 1)}
      today={new Date(2025, 0, 6)}
      selected={selected}
      onSelect={setSelected}
    />
  );
}

API Reference

Calendar forwards react-day-picker’s DayPickerProps — mode, selected, onSelect, month, defaultMonth, disabled, today, and the rest. Use defaultView="months" or defaultView="years" to start in a different view. className, style, classNames, numberOfMonths, captionLayout, navLayout, and showWeekNumber are omitted: styling is owned by propel’s tokens, and those DayPicker layout modes have no styled contract.

The labels prop also accepts labelPreviousYear, labelNextYear, labelPreviousYearPage, labelNextYearPage, labelMonthView, and labelYearView to name the month and year chooser controls for assistive technology.