Skip to main content

Customization

Before diving into all the things you can customize, let's first understand the calendar's components and how they relate to each other.

Anatomy

ComponentDescriptionLayout-related props
CalendarThe main calendar component.calendarSpacing
Calendar.Row.MonthRenders the month row of the calendarcalendarMonthHeaderHeight, getCalendarMonthFormat
Calendar.Row.WeekRenders each week row of the calendar, including the week day names.calendarWeekHeaderHeight, calendarRowHorizontalSpacing, calendarRowVerticalSpacing
Calendar.Item.DayRenders the day item of the calendar (e.g. 1, 2, 3, etc.)calendarDayHeight, getCalendarDayFormat
Calendar.Item.WeekNameRenders the week day name item of the calendar (e.g. Sun, Mon, Tue, etc.)getCalendarWeekDayFormat, calendarFirstDayOfWeek
Calendar.Item.EmptyRenders an empty item to fill the calendar's grid in the start or end of the month.

Customization strategies

There are two main strategies to customize the calendar:

  1. Using the theme prop: the easiest way.
  2. Composing your own calendar: the most flexible way.

Theme prop

The theme prop enables you to change every single style of the base calendar. For most cases, this should be enough to achieve the desired look and feel. Here's an example mimicking Linear's calendar:

import { CalendarTheme } from "@marceloterreiro/flash-calendar";

const linearAccent = "#585ABF";

const linearTheme: CalendarTheme = {
rowMonth: {
content: {
textAlign: "left",
color: "rgba(255, 255, 255, 0.5)",
fontWeight: "700",
},
},
rowWeek: {
container: {
borderBottomWidth: 1,
borderBottomColor: "rgba(255, 255, 255, 0.1)",
borderStyle: "solid",
},
},
itemWeekName: { content: { color: "rgba(255, 255, 255, 0.5)" } },
itemDayContainer: {
activeDayFiller: {
backgroundColor: linearAccent,
},
},
itemDay: {
idle: ({ isPressed, isWeekend }) => ({
container: {
backgroundColor: isPressed ? linearAccent : "transparent",
borderRadius: 4,
},
content: {
color: isWeekend && !isPressed ? "rgba(255, 255, 255, 0.5)" : "#ffffff",
},
}),
today: ({ isPressed }) => ({
container: {
borderColor: "rgba(255, 255, 255, 0.5)",
borderRadius: isPressed ? 4 : 30,
backgroundColor: isPressed ? linearAccent : "transparent",
},
content: {
color: isPressed ? "#ffffff" : "rgba(255, 255, 255, 0.5)",
},
}),
active: ({ isEndOfRange, isStartOfRange }) => ({
container: {
backgroundColor: linearAccent,
borderTopLeftRadius: isStartOfRange ? 4 : 0,
borderBottomLeftRadius: isStartOfRange ? 4 : 0,
borderTopRightRadius: isEndOfRange ? 4 : 0,
borderBottomRightRadius: isEndOfRange ? 4 : 0,
},
content: {
color: "#ffffff",
},
}),
},
};

export const LinearCalendar = memo(() => {
return (
<Calendar
calendarDayHeight={30}
calendarFirstDayOfWeek="sunday"
calendarMonthId={toDateId(new Date())}
calendarRowHorizontalSpacing={16}
calendarRowVerticalSpacing={16}
onCalendarDayPress={(dateId) => console.log(`Pressed date ${dateId}`)}
theme={linearTheme}
/>
);
});

Check out the full source code here.

Composing your own calendar

Sometimes, you need more than just changing styles. You might want to change the layout, add new components, or even remove some of them. In this case, you can easily build your own calendar using the base Calendar components from above.

The sky is the limit here. Here are two demos from the example app:

Windows XP calendar • source codeA nostalgic rebuild to showcase what's possible with Flash Calendar.
Perf calendar • source codeA calendar built to measure re-renders.

Changing the text props

You can override the textProps to control the nested Text components. For example disabling font scaling for accessibility on the Calendar's Day:

import {
Calendar,
useOptimizedDayMetadata,
} from "@marceloterreiro/flash-calendar";
import { Text } from "react-native";
import type { CalendarItemDayWithContainerProps } from "@/components/CalendarItemDay";

import { useRenderCount } from "./useRenderCount";

export const PerfTestCalendarItemDayWithContainer = ({
children,
metadata: baseMetadata,
onPress,
theme,
dayHeight,
daySpacing,
containerTheme,
}: CalendarItemDayWithContainerProps) => {
const metadata = useOptimizedDayMetadata(baseMetadata);
const renderCounter = useRenderCount();

return (
<Calendar.Item.Day.Container
dayHeight={dayHeight}
daySpacing={daySpacing}
isStartOfWeek={metadata.isStartOfWeek}
shouldShowActiveDayFiller={
metadata.isRangeValid && !metadata.isEndOfWeek
? !metadata.isEndOfRange
: false
}
theme={containerTheme}
>
<Calendar.Item.Day
textProps={{ allowFontScaling: false }}
height={dayHeight}
metadata={metadata}
onPress={onPress}
theme={theme}
>
{children}
<Text
style={{
fontSize: 8,
fontStyle: "italic",
textAlign: "center",
color: metadata.state === "active" ? "white" : "black",
}}
>
{"\n"}render: {renderCounter}x
</Text>
</Calendar.Item.Day>
</Calendar.Item.Day.Container>
);
};