shadimage
kanpeki
uicalendarsource: ok · hit

Calendar

npx shadcn add "https://kanpeki.vercel.app/r/calendar.json"
free

Where it goes

calendar

Tags auto-generated by rules on 9/24/2026 — empty means no evidence, not lack of support.

Dependencies

Bundle weight · light
react-aria-components@internationalized/date

Registry dependencies

@kanpeki/cva@kanpeki/custom-plugin

Files4 · source cached 10/14/2026, 3:39:12 PM

src/registry/ui/calendar/index.ts
export * from "./calendar";
export * as Calendar from "./namespace";
export * from "./styles";
src/registry/ui/calendar/calendar.tsx
"use client";

import { getLocalTimeZone, isToday } from "@internationalized/date";
import type { VariantProps } from "cva";
import {
  Calendar,
  composeRenderProps,
  type DateValue,
  Heading,
  CalendarCell as RACCalendarCell,
  CalendarGrid as RACCalendarGrid,
  CalendarGridBody as RACCalendarGridBody,
  CalendarGridHeader as RACCalendarGridHeader,
  CalendarHeaderCell as RACCalendarHeaderCell,
  RangeCalendar,
} from "react-aria-components";
import { CalendarStyles } from "./styles";

export interface CalendarRootProps<T extends DateValue>
  extends React.ComponentProps<typeof Calendar<T>>,
    VariantProps<typeof CalendarStyles.Root> {}

export function CalendarRoot<T extends DateValue>({
  className,
  variant,
  ...props
}: CalendarRootProps<T>) {
  return (
    <Calendar
      className={composeRenderProps(className, (className) =>
        CalendarStyles.Root({
          className,
          variant,
        })
      )}
      data-slot="calendar-root"
      {...props}
    />
  );
}

export interface RangeCalendarRootProps<T extends DateValue>
  extends React.ComponentProps<typeof RangeCalendar<T>>,
    VariantProps<typeof CalendarStyles.Root> {}

export function RangeCalendarRoot<T extends DateValue>({
  className,
  variant,
  ...props
}: RangeCalendarRootProps<T>) {
  return (
    <RangeCalendar
      className={composeRenderProps(className, (className) =>
        CalendarStyles.Root({
          className,
          variant,
        })
      )}
      data-slot="range-calendar-root"
      {...props}
    />
  );
}

export interface CalendarHeaderProps extends React.ComponentProps<"header"> {}

export function CalendarHeader({ className, ...props }: CalendarHeaderProps) {
  return (
    <header
      className={CalendarStyles.Header({ className })}
      data-slot="calendar-header"
      {...props}
    />
  );
}

export interface CalendarMonthProps
  extends React.ComponentProps<typeof Heading> {}

export function CalendarMonth({ className, ...props }: CalendarMonthProps) {
  return (
    <Heading
      {...props}
      className={CalendarStyles.Heading({ className })}
      data-slot="calendar-month"
    />
  );
}

export interface CalendarNavProps extends React.ComponentProps<"nav"> {}

export function CalendarNav({ className, ...props }: CalendarNavProps) {
  return (
    <nav
      {...props}
      className={CalendarStyles.Nav({ className })}
      data-slot="calendar-nav"
    />
  );
}

export interface CalendarGridProps
  extends React.ComponentProps<typeof RACCalendarGrid> {}
export function CalendarGrid({ className, ...props }: CalendarGridProps) {
  return (
    <RACCalendarGrid
      {...props}
      className={CalendarStyles.Grid({ className })}
      data-slot="calendar-grid"
    />
  );
}

export interface CalendarGridHeaderProps
  extends React.ComponentProps<typeof RACCalendarGridHeader> {}
export function CalendarGridHeader(props: CalendarGridHeaderProps) {
  return <RACCalendarGridHeader {...props} data-slot="calendar-grid-header" />;
}

export interface CalendarHeaderCellProps
  extends React.ComponentProps<typeof RACCalendarHeaderCell> {}

export function CalendarHeaderCell({
  className,
  ...props
}: CalendarHeaderCellProps) {
  return (
    <RACCalendarHeaderCell
      {...props}
      className={CalendarStyles.HeaderCell({ className })}
      data-slot="calendar-header-cell"
    />
  );
}

export interface CalendarGridBodyProps
  extends React.ComponentProps<typeof RACCalendarGridBody> {}
export function CalendarGridBody(props: CalendarGridBodyProps) {
  return <RACCalendarGridBody {...props} data-slot="calendar-grid-body" />;
}

export interface CalendarCellProps
  extends React.ComponentProps<typeof RACCalendarCell>,
    Omit<VariantProps<(typeof CalendarStyles)["Cell"]>, "isToday"> {}

export function CalendarCell({
  className,
  shape,
  ...props
}: CalendarCellProps) {
  return (
    <RACCalendarCell
      {...props}
      className={composeRenderProps(className, (className, values) =>
        CalendarStyles.Cell({
          className,
          isToday: isToday(values.date, getLocalTimeZone()),
          shape,
        })
      )}
      data-shape={shape}
      data-slot="calendar-cell"
    />
  );
}
src/registry/ui/calendar/styles.ts
import { cva } from "~/registry/lib/cva";

export const CalendarStyles = {
  Cell: cva({
    base: [
      "relative grid size-8 select-none place-content-center whitespace-nowrap text-accent-foreground text-sm outline-hidden transition",
      "hover:bg-accent hover:text-accent-foreground",
      "disabled:cursor-not-allowed disabled:opacity-50",
      "aria-disabled:cursor-not-allowed aria-disabled:opacity-50",
      "focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",

      "selected:bg-primary selected:text-primary-foreground",
      "group-data-[slot=range-calendar-root]:selected:bg-accent group-data-[slot=range-calendar-root]:selected:text-accent-foreground",
      "group-data-[slot=range-calendar-root]:selection-start:bg-primary group-data-[slot=range-calendar-root]:selection-start:text-primary-foreground",
      "group-data-[slot=range-calendar-root]:selection-end:bg-primary group-data-[slot=range-calendar-root]:selection-end:text-primary-foreground",
    ],
    defaultVariants: {
      shape: "default",
    },
    variants: {
      isToday: {
        true: [
          "after:absolute after:start-1/2 after:bottom-1 after:z-10 after:-translate-x-1/2",
          "after:size-[3px] after:rounded-full after:bg-current",
        ],
      },
      shape: {
        default: [
          "rounded-md group-data-[slot=range-calendar-root]:not-selection-start:not-selection-end:selected:rounded-none",
        ],
        rounded: "rounded-full",
      },
    },
  }),
  Grid: cva({
    base: [
      "w-full",
      "[&_tr]:mt-2 [&_tr]:flex [&_tr]:w-full [&_tr]:justify-between",

      "[&_tr_td[aria-selected=true]]:bg-accent",
      "[&_tr_td[aria-selected=true]:has([data-selection-start])]:rounded-l-md",
      "[&_tr_td[aria-selected=true]:has([data-selection-end])]:rounded-r-md",
      "group-data-[slot=calendar-root]:[&_tr_td[aria-selected=true]]:rounded-md",

      "[&_tr_td[aria-selected=true]:has([data-selection-start][data-shape=rounded])]:rounded-l-full",
      "[&_tr_td[aria-selected=true]:has([data-selection-end][data-shape=rounded])]:rounded-r-full",
      "group-data-[slot=calendar-root]:[&_tr_td:has([data-shape=rounded])[aria-selected=true]]:rounded-full",
    ],
  }),
  Header: cva({
    base: ["flex w-full justify-center gap-1 px-1 pb-5 sm:pb-4"],
  }),
  HeaderCell: cva({
    base: ["w-8 rounded-md font-normal text-[0.8rem] text-muted-foreground"],
  }),
  Heading: cva({
    base: ["font-medium text-sm"],
  }),
  Nav: cva({
    base: ["flex w-full items-center justify-between gap-1"],
  }),
  Root: cva({
    base: ["group"],
    variants: {
      variant: {
        outline: "rounded-md border p-3 shadow-sm",
      },
    },
  }),
};
src/registry/ui/calendar/namespace.ts
export {
  CalendarCell as Cell,
  CalendarGrid as Grid,
  CalendarGridBody as GridBody,
  CalendarGridHeader as GridHeader,
  CalendarHeader as Header,
  CalendarHeaderCell as HeaderCell,
  CalendarMonth as Month,
  CalendarNav as Nav,
  CalendarRoot as Root,
  RangeCalendarRoot as Range,
} from "./calendar";

More from kanpeki

Browse all components in this library →