Tips and Tricks
Exploring the libary and props
All props from Flash Calendar are documented and prefixed with calendar
to
increase IDE discoverability. After installing the package, just type <Calendar calendar
and open your autocomplete to see the available options.
Date IDs vs Dates
Date and timezones are a confusing topic. This is why Flash Calendar is careful to name things consistently. All props and callbacks used by the library follows the same convention of using a date ID instead of a Date
.
Date ID is a simple YYYY-MM-DD
representation of a given date. Flash Calendar exposes two functions to convert between Date and Date ID.
import { toDateId, fromDateId } from "@marceloterreiro/flash-calendar";
const januaryFirstAsDate = fromDateId("2024-01-01"); // Date object
const januaryFirstAsId = toDateId(januaryFirstAsDate); // "2024-01-01"
It's highly recommended to use toDateId
and fromDateId
when interacting with the library. These functions were purposefully created to avoid timezones and other date-related issues.
For instance, consider this code:
const endOfJanuary = new Date("2024-01-31");
// ❌ Don't do this, for some dates and timezones, this will return unexpected results
const endOfJanuaryId = endOfJanuary.toISOString().slice(0, 10); // 2024-02-01 or 2024-01-31, depending on the timezone
The code above is not completely safe. It can return 2024-02-01
for some timezones. Instead, use toDateId
:
const endOfJanuaryId = toDateId("2024-01-31");
const endOfJanuary = fromDateId(endOfJanuaryId); // Date object
These two convertions functions were battle-tested to return the expected results, regardless of the timezone.
Programmatically scrolling to a date
Flash Calendar exposes a ref
that allows imperative scrolling to a desired
date (.scrollToDate
), a month (.scrollToMonth
), or an offset
(.scrollToOffset
).
import { addMonths, subMonths, startOfMonth } from "date-fns";
import type { CalendarListRef } from "@marceloterreiro/flash-calendar";
import { Calendar, toDateId } from "@marceloterreiro/flash-calendar";
import { useRef, useState } from "react";
import { Button, Text, View } from "react-native";
export function ImperativeScrolling() {
const [currentMonth, setCurrentMonth] = useState(startOfMonth(new Date()));
const ref = useRef<CalendarListRef>(null);
const onCalendarDayPress = useCallback((dateId: string) => {
ref.current?.scrollToDate(fromDateId(dateId), true);
}, []);
return (
<View style={{ paddingTop: 20, flex: 1 }}>
<View style={{ flexDirection: "row", gap: 12 }}>
<Button
onPress={() => {
const pastMonth = subMonths(currentMonth, 1);
setCurrentMonth(pastMonth);
ref.current?.scrollToMonth(pastMonth, true);
}}
title="Past month"
/>
<Text>Current: {toDateId(currentMonth)}</Text>
<Button
onPress={() => {
const nextMonth = addMonths(currentMonth, 1);
setCurrentMonth(nextMonth);
ref.current?.scrollToMonth(nextMonth, true);
}}
title="Next month"
/>
</View>
<View style={{ flex: 1, width: "100%" }}>
<Calendar.List
calendarInitialMonthId={toDateId(currentMonth)}
onCalendarDayPress={onCalendarDayPress}
ref={ref}
/>
</View>
</View>
);
}
You can also pass an additionalOffset
to fine-tune the scroll position:
.scrollToDate(fromDateId("2024-07-01"), true, { additionalOffset: 4 })
Setting Border Radius to Calendar.Item.Day
To apply a border radius to the Calendar.Item.Day
component, it's necessary to
specify the radius for all four corners. Here's an example of how to achieve
this:
itemDay: {
base: () => ({
container: {
borderTopRightRadius: 10,
borderBottomRightRadius: 10,
borderTopLeftRadius: 10,
borderBottomLeftRadius: 10,
},
}),
}
Avoiding dark mode
If your app doesn't support dynamic themes, you can override Flash Calendar's
color scheme by passing a calendarColorScheme
prop:
export const LightModeOnly = () => {
const { calendarActiveDateRanges, onCalendarDayPress } = useDateRange({
startId: "2024-02-04",
endId: "2024-02-09",
});
return (
<Calendar
calendarActiveDateRanges={calendarActiveDateRanges}
calendarColorScheme="light"
calendarMonthId={toDateId(startOfThisMonth)}
onCalendarDayPress={onCalendarDayPress}
/>
);
};
When set, Flash Calendar's theming system will use this scheme instead of the user system's theme.
Note: you should avoid using this prop. Instead, your app should support dynamic themes that react to the user's system preferences. The prop is provided as an escape hatch for apps that doesn't support dynamic themes yet.
Listening to the visible months
You can listen to which months are currently visible by hooking into the onViewableItemsChange
prop:
export const ListenToVisibleMonth = () => {
const [selectedDate, setSelectedDate] = useState(today);
const [visibleMonth, setVisibleMonth] = useState(today);
const handleViewableItemsChanged = useCallback<
NonNullable<FlashListProps<CalendarMonth>["onViewableItemsChanged"]>
>(({ viewableItems }) => {
const firstVisibleItem = viewableItems.find((item) => item.isViewable);
if (firstVisibleItem) {
setVisibleMonth(firstVisibleItem.item.id);
}
}, []);
return (
<View style={{ flex: 1, gap: 24 }}>
<View style={{ gap: 12 }}>
<Text>Selected date: {selectedDate}</Text>
<Text>Visible month: {visibleMonth}</Text>
</View>
<Calendar.List
calendarActiveDateRanges={[{ startId: today, endId: today }]}
calendarInitialMonthId="2024-11-01"
onCalendarDayPress={setSelectedDate}
onViewableItemsChanged={handleViewableItemsChanged}
/>
</View>
);
};