Skip to main content

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>
);
};