Calendar
A month grid calendar with single date and range selection. Tap the header to quickly jump to any year and month.
Su
Mo
Tu
We
Th
Fr
Sa
Web preview — components render natively on iOS & Android
import { Calendar } from "@/components/ui/calendar";
export function MyScreen() {
const [date, setDate] = useState<Date | undefined>();
return (
<Calendar
selected={date}
onSelect={setDate}
/>
);
}Installation#
npx @aniui/cli add calendarUsage#
app/index.tsx
import { Calendar } from "@/components/ui/calendar";
export function MyScreen() {
const [date, setDate] = useState<Date | undefined>();
return (
<Calendar
selected={date}
onSelect={setDate}
/>
);
}Year & Month Picker#
Tap the month/year header to open the year grid. Select a year, then a month to quickly navigate to any date.
Range Selection#
Su
Mo
Tu
We
Th
Fr
Sa
Click to select start date
Web preview — components render natively on iOS & Android
<Calendar
rangeStart={start}
rangeEnd={end}
onRangeChange={(s, e) => {
setStart(s);
setEnd(e);
}}
/>Props#
PropTypeDefault
selectedDate—onSelect(date: Date) => void—rangeStartDate—rangeEndDate—onRangeChange(start: Date, end: Date | undefined) => void—minDate—maxDate—classNamestring—Accessibility#
- Date selection with day/month/year navigation.
- Each day cell is focusable with
accessibilityLabelfor the full date. - Navigation buttons for previous/next month are labeled for screen readers.
Source#
components/ui/calendar.tsx
import React, { useState } from "react";
import { View, Text, Pressable } from "react-native";
import { cn } from "@/lib/utils";
export interface CalendarProps {
className?: string;
selected?: Date;
onSelect?: (date: Date) => void;
rangeStart?: Date;
rangeEnd?: Date;
onRangeChange?: (start: Date, end: Date | undefined) => void;
min?: Date;
max?: Date;
}
const DAYS = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
const MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
const same = (a: Date, b: Date) => a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
type Mode = "days" | "months" | "years";
export function Calendar({ className, selected, onSelect, rangeStart, rangeEnd, onRangeChange, min, max }: CalendarProps) {
const [viewing, setViewing] = useState(() => selected ?? rangeStart ?? new Date());
const [mode, setMode] = useState<Mode>("days");
const year = viewing.getFullYear(), month = viewing.getMonth();
const handlePress = (day: number) => {
const date = new Date(year, month, day);
if ((min && date < min) || (max && date > max)) return;
if (onRangeChange) {
if (!rangeStart || rangeEnd || date < rangeStart) onRangeChange(date, undefined);
else onRangeChange(rangeStart, date);
}
onSelect?.(date);
};
const handleHeaderPress = () => setMode(mode === "days" ? "years" : "days");
const pickYear = (y: number) => { setViewing(new Date(y, month, 1)); setMode("months"); };
const pickMonth = (m: number) => { setViewing(new Date(year, m, 1)); setMode("days"); };
const decadeStart = Math.floor(year / 12) * 12;
const label = new Date(year, month).toLocaleString("default", { month: "long", year: "numeric" });
// ... renders year grid, month grid, and day grid based on mode
}