How to build a month calendar in React Native
By the LumioUI team · Updated
A month calendar is a 7-column grid of days with blanks before the 1st, a header to page between months, and a mark on days that have something on. You don't need a library to build one in React Native: work out the grid from the first weekday and the number of days in the month, render it with flexWrap, and key every day by its ISO date. The LumioUI Calendar adds busy-day dots, swiping between months, folding into a single week and an agenda that swaps below.
Recording of the LumioUI Calendar component (Pro).
How a month calendar works
- The grid.
new Date(y, m, 1).getDay()gives the number of blanks before the 1st;new Date(y, m + 1, 0).getDate()gives the number of days. Pad the end to a multiple of 7. - Key days by ISO string.
"2026-10-14"compares and looks up cleanly. Build it from local date parts, nottoISOString(), which converts to UTC and can shift the day. - Busy days. Turn your events into a map of date to count, and draw up to three dots under each day.
- Paging. Keep the visible month in state; next and previous set it to the 1st of the neighbouring month. Swiping can drive the same state.
- Accessibility. Each day is a button with a full spoken date, its event count and
selectedstate. The month title is a header.
import * as React from "react";
import { Pressable, StyleSheet, Text, View } from "react-native";
const WEEKDAYS = ["S", "M", "T", "W", "T", "F", "S"];
const iso = (d: Date) =>
`${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
/** The month as 7-day rows: leading and trailing blanks are null. */
function monthGrid(month: Date): (Date | null)[] {
const y = month.getFullYear(), m = month.getMonth();
const cells: (Date | null)[] = Array(new Date(y, m, 1).getDay()).fill(null); // 0 = Sunday
const days = new Date(y, m + 1, 0).getDate();
for (let d = 1; d <= days; d++) cells.push(new Date(y, m, d));
while (cells.length % 7) cells.push(null);
return cells;
}
export function MonthCalendar({ events, selected, onSelect }: {
events: Record<string, number>; // "yyyy-MM-dd" -> event count
selected: string;
onSelect: (day: string) => void;
}) {
const [month, setMonth] = React.useState(() => new Date());
const shift = (n: number) => setMonth((d) => new Date(d.getFullYear(), d.getMonth() + n, 1));
const today = iso(new Date());
return (
<View>
<View style={styles.header}>
<Pressable onPress={() => shift(-1)} accessibilityLabel="Previous month" hitSlop={12}><Text>‹</Text></Pressable>
<Text accessibilityRole="header" style={styles.title}>
{month.toLocaleDateString(undefined, { month: "long", year: "numeric" })}
</Text>
<Pressable onPress={() => shift(1)} accessibilityLabel="Next month" hitSlop={12}><Text>›</Text></Pressable>
</View>
<View style={styles.grid}>
{WEEKDAYS.map((w, i) => <Text key={i} style={styles.weekday}>{w}</Text>)}
{monthGrid(month).map((day, i) => {
if (!day) return <View key={i} style={styles.cell} />;
const key = iso(day);
const count = events[key] ?? 0;
const on = key === selected;
return (
<Pressable key={key} style={styles.cell} onPress={() => onSelect(key)}
accessibilityRole="button" accessibilityState={{ selected: on }}
accessibilityLabel={`${day.toDateString()}${count ? `, ${count} events` : ""}`}>
<View style={[styles.day, on && styles.dayOn]}>
<Text style={[key === today && styles.today, on && styles.textOn]}>{day.getDate()}</Text>
</View>
<View style={styles.dots}>
{Array.from({ length: Math.min(count, 3) }, (_, d) => <View key={d} style={styles.dot} />)}
</View>
</Pressable>
);
})}
</View>
</View>
);
}
const styles = StyleSheet.create({
header: { flexDirection: "row", alignItems: "center", justifyContent: "space-between", paddingVertical: 12 },
title: { fontSize: 17, fontWeight: "600" },
grid: { flexDirection: "row", flexWrap: "wrap" },
weekday: { width: `${100 / 7}%`, textAlign: "center", opacity: 0.5, paddingBottom: 6 },
cell: { width: `${100 / 7}%`, alignItems: "center", paddingVertical: 4 },
day: { width: 36, height: 36, borderRadius: 18, alignItems: "center", justifyContent: "center" },
dayOn: { backgroundColor: "#111" },
textOn: { color: "#fff" },
today: { fontWeight: "700" },
dots: { flexDirection: "row", gap: 2, height: 6, marginTop: 2 },
dot: { width: 4, height: 4, borderRadius: 2, backgroundColor: "#888" },
}); Need the week to start on Monday? Use (getDay() + 6) % 7 for the blanks and reorder the weekday labels.
The quick way: LumioUI Calendar
The Calendar shows up to three coloured dots per day, swipes between months, folds into the selected week, jumps back with a Today pill and swaps the agenda below as you pick a day. It's part of LumioUI Pro.
import * as React from "react";
import { Calendar } from "@/components/ui/calendar/calendar";
const EVENTS = [
{ date: "2026-10-14", category: "work", title: "Design review" },
{ date: "2026-10-14", category: "personal", title: "Climbing" },
{ date: "2026-10-21", category: "work", title: "Launch" },
];
export function Schedule() {
const [day, setDay] = React.useState<Date>(new Date());
return (
<Calendar
selected={day}
onSelect={setDay}
events={EVENTS}
categories={{ work: "teal", personal: "orange" }}
renderAgenda={(date, events) => <DayAgenda date={date} events={events} />}
/>
);
} selectedandonSelectmake it controlled (ISO string orDate);defaultSelectedlets it manage itself.eventswithcategoriescolour the dots;markedDatesis a shorthand for a single dot.availableDatesturns the grid into a map of open days for booking;disabledDates,minDateandmaxDatelimit what can be picked.collapsedfolds it to one week. For a week strip see Calendar Strip, and for check-in and check-out, Date Range Picker.
Add it to your app
LumioUI's CLI copies the component's source into your project, so it's yours to read and change. Run init once per project:
npx @lumioui/cli init
npx @lumioui/cli add pro/calendar It works in Expo (SDK 54+) and bare React Native, with Reanimated 4 and NativeWind. See the installation guide and the full Calendar API.Calendar is part of LumioUI Pro: Pro installs need your licence key (CLI guide, pricing).
Related components
- Calendar Strip (Pro)
- Date Range Picker (Pro)
- Slot Picker (Pro)
- Agenda List (Pro)
More React Native guides
- How to make a bottom sheet in React Native
- Animated tabs with a sliding indicator in React Native
- How to show toast notifications in React Native
- Skeleton loading screens in React Native
- How to add dark mode and a theme toggle in React Native
- How to build an accordion in React Native
- How to make a side drawer menu in React Native
- How to make an animated heart like button in React Native
- How to make a modal dialog in React Native
- How to build an animated carousel in React Native
- How to make an animated splash screen in React Native and Expo