Shalitha Suranga Programmer | Author of Neutralino.js | Technical Writer

Create customized and shareable calendars in React Native

13 min read 3883

Create customized and shareable calendars in React Native

Mobile app developers use various GUI elements in their apps, like buttons, lists, input boxes, and text regions within app screens. Sometimes, we need complex GUI elements, such as calendars, timetables, agenda views, etc.

The React Native framework offers inbuilt components for adding primary GUI elements, but for designing complex GUI components, we’ll have to either build them from scratch with inbuilt core elements or reuse pre-implemented components from third-party libraries.

The react-native-calendars library offers pre-implemented, customizable components for adding calendars to your React Native apps. You can use these calendar components for creating customized shareable calendars to display date-based data and capture date values from the user.

In this tutorial, I will explain how to create customized shareable calendars with react-native-calendars, and let you know about several alternative packages for creating customizable calendars.

Jump ahead:

Highlighted features of react-native-calendars

This calendar library offers the following highlighted features for adding customizable high-quality calendar components:

Multiple components for your needs

The library offers various components for creating calendars, agenda views, and timelines. Here are the popular ones for creating calendars:

  • Calendar: Renders a calendar component with inbuilt month navigation support
  • CalendarList: A modern, stylish component that renders a semi-infinite, scrollable calendar list
  • Agenda: A complex calendar component helps developers implement timetables, appointment tables, and date schedulers

Cross-platform support and consistent look and feel

Cross-platform support is indeed a crucial fact to consider while selecting React Native libraries. This package officially supports Android and iOS platforms and offers a consistent look and feel with primary React Native components (Text, TouchableOpacity, etc.) that look almost the same on both operating systems.

Extensive customizations and features

Each library component exposes several props to customize the particular component’s behavior and features. For example, this package lets you customize the calendar component’s fonts and colors by providing a theme object. Also, you can mark individual dates and date ranges with inbuilt customizable indicators and custom shapes.

It’s possible to achieve advanced styling customizations by overriding stylesheet definitions. So, if you use your own theme for your app, using the existing theme colors is easy. This package also lets you attach callbacks for various events, so building a customized, modern date picker is feasible.

react-native-calendars tutorial

Now, we already know an overview of the calendars package. Let’s start using it practically in a React Native app to become more familiar by testing its features. You can use the upcoming code examples directly in an existing app or create a new experimental app to continue with the tutorial.

If you would like to get started with a new project, create one with the following command:

npx react-native init CalendarsExample
cd CalendarsExample

Next, run the app to make sure that everything works fine:

npx react-native run-android
# --- or ---
npx react-native run-ios

Enter the following command and install react-native-calendars:

npm install react-native-calendars
# --- or ---
yarn install react-native-calendars

Let’s start with the Calendar component and learn its customizations and features in depth!

Creating a basic calendar

The Calendar component lets you render a traditional calendar element that supports month navigation. There are no mandatory props here — use the component with no props in App.js to create a calendar with the default setup:

import React from 'react';
import {
  SafeAreaView,
  ScrollView,
  StyleSheet,
  View,
} from 'react-native';
import { Calendar } from 'react-native-calendars';

function App() {
  return (
    <SafeAreaView style={styles.container}>
      <Calendar />
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center'
  },
});

export default App;

The above code will render a calendar component with default features and styles, as shown in the following preview:

Our calendar component with the default features and styles
The calendar component in Android

Note: Selecting a date won’t render a highlighted background style as everyone expects as an inbuilt feature — we’ll have to write some code for it. I will explain how to highlight the selected date in the Marking individual days section.

Attaching touch event callbacks

The calendar component is interactive, so users can perform actions by pressing the month navigation arrows and day elements.
This library exposes several props to attach callbacks for these crucial user actions. Look at the following sample callback implementations:

<Calendar
  onDayPress={(day) => console.log('onDayPress', day) }
  onDayLongPress={(day) => console.log('onDayLongPress', day) }
  onMonthChange={(date) => console.log('onMonthChange', date) }
  onPressArrowLeft={(goToPreviousMonth) => {
    console.log('onPressArrowLeft'); goToPreviousMonth();
  }}
  onPressArrowRight={(goToNextMonth) => {
    console.log('onPressArrowRight'); goToNextMonth();
  }}
/>

Handlers are executed on the following inputs:

  • onDayPress: When the user taps a specific day element
  • onDayLongPress: When the user long presses a day element
  • onMonthChange: When the calendar month changes
  • onPressArrowLeft: When the user clicks the left-side month navigation arrow
  • onPressArrowRight: When the user clicks the right-side month navigation arrow

Run the above code snippet, inspect the code, modify it, and become familiar with all our callbacks. For example, you can get the user-pressed date as follows:

onDayPress={(day) => console.log('Selected date: ', day.dateString) }

Experiment with all the other callbacks by attaching function implementations.

Setting initial, min, and max dates

In some scenarios, using a default calendar with callback implementations is not enough to satisfy app specifications. Sometimes, we need to set the initially selected month and minimum/maximum end date. The Calendar component offers props to set initial, min, and max dates.



Assume that you need to make a calendar component that initially shows December 2022 and lets you press only a day between the 1st of December 2022 and the 30th of January 2023. The following code snippet satisfies this specification:

<Calendar
  initialDate="2022-12-01"
  minDate="2022-12-01"
  maxDate="2023-01-30"
  disableAllTouchEventsForDisabledDays={true}
/>

Here, the disableAllTouchEventsForDisabledDays boolean prop helps us disable the touch feedback for disabled date elements. Run the app code snippet and compare the result with the above specification:

Our calendar component with disabled date elements
Setting initial, min, and max dates

Making the calendar component reusable

If you need to use the above calendar in several places, creating a reusable component is undoubtedly a good practice:

import React from 'react';
import {
  SafeAreaView,
  ScrollView,
  StyleSheet,
  View,
} from 'react-native';
import { Calendar } from 'react-native-calendars';

function CustomCalendar(props) {
  return (
    <Calendar
      initialDate="2022-12-01"
      minDate="2022-12-01"
      maxDate="2023-01-30"
      disableAllTouchEventsForDisabledDays={true}
      {...props}
    />
  );
}

function App() {
  return (
    <SafeAreaView style={styles.container}>
      <CustomCalendar onDayPress={(day) => console.log(`Date pressed: ${day.dateString}`)} />
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
  },
});

export default App;

The above CustomCalendar component uses custom initial, min, and max dates. It also accepts Calendar props for further customizations. We’ll discuss how to develop a reusable date picker component in an upcoming section — then, you can learn more about creating shareable calendars.

Supported calendar UI customizations

The calendar component consists of View, TouchableOpacity, and Text-like inbuilt framework components, unlike the platform-specific, native date picker component in Android and iOS. So, it offers numerous props for extensive UI customization.

For example, you can disable the left and right arrows with the disableLeftArrow and disableRightArrow boolean props in the previous CustomCalendar implementation:

function CustomCalendar(props) {
  return (
    <Calendar
      initialDate="2022-12-01"
      minDate="2022-12-01"
      maxDate="2022-12-31"
      disableAllTouchEventsForDisabledDays={true}
      disableArrowLeft={true}
      disableArrowRight={true}
      {...props}
    />
  );
}

Now, you will only see December 2022, since month-by-month navigation is not possible:

Disabling the month navigation icons
Disabling the month navigation icons

Hiding both arrows is also possible with the hideArrows boolean prop:

function CustomCalendar(props) {
  return (
    <Calendar
      initialDate="2022-12-01"
      minDate="2022-12-01"
      maxDate="2022-12-31"
      disableAllTouchEventsForDisabledDays={true}
      hideArrows={true}
      {...props}
    />
  );
}

By default, week rows start with Sunday. You can start every week row with Monday and show week numbers on the calendar with the following props:

firstDay={1}
showWeekNumbers={true}

Look at the following preview:

Changing the default first weekday and showing the week numbers
Changing the default first weekday and showing the week numbers

Using a custom month format is also possible with the dateFormat prop. For example, the following setup will render Dec/2020 -like month strings:

monthFormat="MMM/yyyy"

Browse the official documentation and identify all supported calendar props that help customizing the calendar UI.

Marking individual days

Look at a physical paper calendar on your table or wall. You will see that several day boxes are marked with background colors or symbols to indicate special days or national holidays. How can we mark day elements in the Calendar component similar to the physical one?

The Calendar component lets you mark dates via the markedDates prop. Let’s learn how to mark individual days first! Use the following code for CustomCalendar:

function CustomCalendar(props) {
  const marked = {
    '2022-12-10': { marked: true },
    '2022-12-12': { selected: true }
  };
  return (
    <Calendar
      initialDate="2022-12-01"
      minDate="2022-12-01"
      maxDate="2022-12-31"
      disableAllTouchEventsForDisabledDays={true}
      markedDates={marked}
      {...props}
    />
  );
}

If the marked boolean prop is set to true, the component will render a dot on a specific day; if the selected boolean prop is set to true, the component will render a filled circle around the particular day element. Look at the following preview:

Marking day elements with a circle shape and a dot indicator
Marking day elements with a circle shape and a dot indicator

It’s possible to customize the above indicators and shapes as follows:

const marked = {
    '2022-12-10': { marked: true, dotColor: 'red' },
    '2022-12-12': { selected: true, selectedColor: '#aa2222', selectedTextColor: 'yellow' },
    '2022-12-13': {
      marked: true,
      selected: true,
      selectedColor: '#222222',
      selectedTextColor: 'yellow',
      dotColor: 'white'
    }
  };

Once you use the above definition for marked, you will see the following result:

Using custom styles for default marking indicators
Using custom styles for default marking indicators

The markedDates prop helps us to mark the currently pressed day element with a simple implementation. We can store the current date in the component state and update the markedDates object accordingly, as shown in the following code:

import React, { useState, useMemo } from 'react';
import {
  SafeAreaView,
  ScrollView,
  StyleSheet,
  View,
} from 'react-native';
import { Calendar } from 'react-native-calendars';

function CustomCalendar(props) {
  const initDate = '2022-12-01';
  const [selected, setSelected] = useState(initDate);
  const marked = useMemo(() => ({
    [selected]: {
      selected: true,
      selectedColor: '#222222',
      selectedTextColor: 'yellow',
    }
  }), [selected]);
  return (
    <Calendar
      initialDate={initDate}
      markedDates={marked}
      onDayPress={(day) => {
        setSelected(day.dateString);
        props.onDaySelect && props.onDaySelect(day);
      }}
      {...props}
    />
  );
}

function App() {
  return (
    <SafeAreaView style={styles.container}>
      <CustomCalendar onDaySelect={(day) => console.log(`Date selected: ${day.dateString}`)}/>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
  },
});

export default App;

Here, we used the useMemo Hook to memoize the marked object and recompute it only if selected is changed (for performance optimization). See how the above code implements selectable day elements:

Implementing a date selection feature
Implementing a date selection feature

The marked boolean element renders a circle shape with several pre-included styles, but this library is flexible enough to offer the customStyles prop for building a developer-defined shape style. The following code snippet renders a green rectangle when you press a day element:

function CustomCalendar(props) {
  const initDate = '2022-12-01';
  const [selected, setSelected] = useState(initDate);
  const marked = useMemo(() => ({
    [selected]: {
      customStyles: {
        container: {
          backgroundColor: 'green',
          borderRadius: 0,
        },
        text: {
          color: 'white',
        }
      }
    }
  }), [selected]);
  return (
    <Calendar
      initialDate="2022-12-01"
      markingType="custom"
      markedDates={marked}
      onDayPress={(day) => {
        setSelected(day.dateString);
        props.onDaySelect && props.onDaySelect(day);
      }}
      {...props}
    />
  );
}

Here, we used markingType="custom" to turn on the custom markers feature. The multi-dot marking type lets us add multiple dot indicators into day elements, as if to indicate events on a particular day, as shown in the following code:

import React from 'react';
import {
  SafeAreaView,
  ScrollView,
  StyleSheet,
  View,
} from 'react-native';
import { Calendar } from 'react-native-calendars';

function App() {
  const running = {key: 'running', color: 'blue'};
  const cycling = {key: 'cycling', color: 'green'};
  const walking = {key: 'walking', color: 'orange'};
  const marked = {
    '2022-12-01': {
      dots: [running, walking]
    },
    '2022-12-02': {
      dots: [running, walking, cycling]
    }
  };
  return (
    <SafeAreaView style={styles.container}>
      <Calendar
        initialDate="2022-12-01"
        markingType="multi-dot"
        markedDates={marked}
      />
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
  },
});

export default App;

The above code generates a calendar component with multiple dot indicators in day elements:

Adding multiple custom dot indicators
Adding multiple custom dot indicators

Marking date ranges

We can write some code and implement a date-range marking style ourselves with customStyles, but this library boosts developers’ productivity by offering an inbuilt date-range marking feature.
For example, you can mark the first ten days of December 2022 with the inbuilt range-marking feature as follows:

function App() {
  const getMarked = () => {
    let marked = {};
    for(let i = 1; i <= 10; i++) {
      let day = i.toString().padStart(2, '0');
      marked[`2022-12-${day}`] = {
        startingDay: i == 1,
        endingDay: i == 10,
        color: 'yellow',
        textColor: '#aaa',
        disabled: true,
      };
    }
    return marked;
  };

  return (
    <SafeAreaView style={styles.container}>
      <Calendar
        initialDate="2022-12-01"
        markingType="period"
        markedDates={getMarked()}
      />
    </SafeAreaView>
  );
};

Here, we use the getMarked function to generate a ten-day-long period. The period start/end edge styling will change according to the startingDay and endingDay booleans. Once you run the above code snippet, you should see the following result:

Marking a date range with a preferred background color
Marking a date range with a preferred background color

It’s possible to build a date range selector component with this period marking feature. You can see a sample implementation from my GitHub repository.

This library offers an inbuilt multi-period-marking feature, too. Look at the following code:

function App() {
  const getMarked = () => {
    let marked = {};
    for(let i = 1; i <= 10; i++) {
      let day = i.toString().padStart(2, '0');
      let periods = [
        {
          startingDay: i == 1,
          endingDay: i == 10,
          color: 'teal',
        },
        (i >= 2 && i <= 6) && {
          startingDay: i == 2,
          endingDay: i == 6,
          color: 'orange',
        }
      ];
      marked[`2022-12-${day}`] = {
        periods
      };
    }
    return marked;
  };
  return (
    <SafeAreaView style={styles.container}>
      <Calendar
        initialDate="2022-12-01"
        markingType="multi-period"
        markedDates={getMarked()}
      />
    </SafeAreaView>
  );
};

The above App component implementation renders two date period lines between the 1st and the 10th of December 2022:

Using the multi-period marking feature
Using the multi-period marking feature

Want to display a quick summary of a few bookings in a calendar? Try the multi-period marking type.

Customizing the calendar theme

Now, we already know how to do basic UI customizations; we changed the first weekday, disabled/hid the month navigation arrows, changed the month format, etc. What if we need to change the day element, header, weekday name colors, and fonts?

As mentioned in the highlighted features section, this library lets you change the look and feel by passing a custom theme object. If you feel that using a custom theme object is limiting, you can directly override stylesheet definitions of the calendar component. Moreover, you can customize the calendar component’s container styles.

First, look at the following example code and learn how to customize the calendar’s look and feel via the theme prop, and the calendar container’s styles with the style prop:

<Calendar
  initialDate="2022-12-01"
  style={{
    borderRadius: 5,
    margin: 12,
    elevation: 5,
    borderWidth: 4,
    borderColor: 'rgba(100, 100, 100, 0.2)'
  }}
  theme={{
    calendarBackground: '#222',
    dayTextColor: '#fff',
    textDisabledColor: '#444',
    monthTextColor: '#888'
  }}
/>

The above code snippet applies a dark color scheme to the calendar via the theme prop and some styling enhancements for the calendar container via the style prop. Look at the following preview:

Implementing a custom dark color theme for the calendar
Implementing a custom dark color theme for the calendar

The theme prop offers a way to customize text styles and colors. But, we sometimes need to do advanced customizations using more styling definitions. For such scenarios, it’s possible to override stylesheet definitions. For example, you can override the calendar header style as follows:

theme={{
  'stylesheet.calendar.header': {
    headerContainer: {
      flexDirection: 'row',
      backgroundColor: '#eee',
      borderRadius: 12
    },
  }
}}

Look at your style.ts files by browsing the source and identify all style definitions that you can override via theme. Check the end of a particular style.ts file to find the stylesheet identifier (i.e., style.calendar.header).

Styling weekend day headers

We can use stylesheet-overriding in the theme prop to apply different styles for weekend day headers. For example, we can use green for Saturday headers and red for Sunday headers with the following theme object:

theme={{
  'stylesheet.calendar.header': {
    dayTextAtIndex0: {
      color: 'red'
    },
    dayTextAtIndex6: {
      color: 'green'
    }
  }
}}

Here is the preview for the above setup:

Changing weekend days' header styles via the theme object
Changing weekend days’ header styles via the theme object

Using two different colors for weekend day numbers is also a good UI/UX improvement. But, the library doesn’t support customizing indexed day element styling as we used for customizing day headers via the theme prop. So, we have to style weekend day numbers with markedDates until the maintainers and contributors work on this pull request’s idea.

Creating scrollable calendar lists

Modern mobile apps often use more swipe events than traditional taps. For example, every popular social media app typically loads more posts when the user swipes up  —  not when the user taps a load-more-posts button. The Calendar component is designed to navigate with arrows and see one month at a time, but you can use the CalendarList component to create a modern infinite scroller-like calendar component.
Run the following code:

import React from 'react';
import {
  SafeAreaView,
  ScrollView,
  StyleSheet,
  View,
} from 'react-native';
import { CalendarList } from 'react-native-calendars';

function App() {
  return (
    <SafeAreaView style={styles.container}>
      <CalendarList/>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
  },
});

export default App;

Now, you can see a scrollable calendar:

Using CalendarList to create a scrollable calendar
Using CalendarList to create a scrollable calendar

However, the Calendar component is minimal, traditional, and generic, so consider selecting CalendarList only if the screen is calendar-oriented (i.e., displaying within a Modal) and users need to see multiple months at a time. The CalendarList component accepts all Calendar props and some additional props.

Using the Agenda component for advanced use cases

In some scenarios, we need to implement agenda views in our React Native apps. The Calendar and CalendarList components support adding multi-period markers via the markedDates prop, but the calendar component doesn’t have enough space to add a title and description for each marker line. So, implementing an agenda view with Calendar or CalendarList component is not a good decision.

The Agenda component lets you create agenda views by displaying records for each day in a second view that gets opened after tapping a day element.

Run the following code to display a sample agenda view:

import React from 'react';
import {
  SafeAreaView,
  ScrollView,
  StyleSheet,
  Text,
  TouchableOpacity,
} from 'react-native';
import { Agenda } from 'react-native-calendars';

function App() {
  return (
    <SafeAreaView style={styles.container}>
      <Agenda
        selected="2022-12-01"
        items={{
          '2022-12-01': [{name: 'Cycling'}, {name: 'Walking'}, {name: 'Running'}],
          '2022-12-02': [{name: 'Writing'}]
        }}
        renderItem={(item, isFirst) => (
          <TouchableOpacity style={styles.item}>
            <Text style={styles.itemText}>{item.name}</Text>
          </TouchableOpacity>
        )}
      />
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center'
  },
  item: {
    backgroundColor: 'white',
    flex: 1,
    borderRadius: 5,
    padding: 10,
    marginRight: 10,
    marginTop: 17,
  },
  itemText: {
    color: '#888',
    fontSize: 16,
  }
});

export default App;

Here, we used the Agenda component by providing day items via the items prop. You can use the renderItem callback to design a GUI for each day item. Check other callbacks and supported props from the official documentation on this GitHub repository.

Once you run the above code, you will see a minimal agenda view as follows:

The Agenda component preview in Android
The Agenda component preview in Android

We hardcoded several day items for demonstration purposes, but you can also use a remote web server to fetch data for your agenda view in production apps.

How to place calendar components

There are two key ways to render any GUI element in mobile apps: rendering on an app screen surface or rendering in a popup. I demonstrated all the above calendar component customizations directly on an app screen. If you use a calendar component as a user input element, showing it in a popup saves the current app screen’s space and improves overall usability.

Look at the following code:

import React, { useState } from 'react';
import {
  SafeAreaView,
  ScrollView,
  StyleSheet,
  View,
  Modal,
  Button,
} from 'react-native';
import { Calendar } from 'react-native-calendars';

function DatePicker({visible, onDateSelected}) {
  return (
    <Modal visible={visible} transparent={true} animationType="fade">
      <View style={styles.overlay}>
        <Calendar onDayPress={onDateSelected}/>
      </View>
    </Modal>
  );
}

function App() {
  const [modalVisible, setModalVisible] = useState(false);
  return (
    <SafeAreaView style={styles.container}>
      <Button title="Show Modal" onPress={() => setModalVisible(true)}/>
      <DatePicker
        visible={modalVisible}
        onDateSelected={() => setModalVisible(false)}/>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center'
  },
  overlay: {
    flex: 1,
    justifyContent: 'center',
    padding: 40,
    backgroundColor: 'rgba(100, 100, 100, 0.6)',
  },
});

export default App;

Here, we created the reusable DatePicker component and showed a calendar component within a Modal. Once you tap the button, the modal will appear with an overlay and a calendar component, as shown in the following preview:

Showing the calendar within the Modal component
Showing the calendar within the Modal component

react-native-calendars components

In this tutorial, we discussed three popular components that react-native-calendars offers:

  1. Calendar
  2. CalendarList
  3. Agenda

We mainly focused on the Calendar component because it lets you create customizable and shareable calendar components in your React Native apps, but the library offers a week calendar via WeekCalendar, a timeline implementation via Timeline, etc.

Check out all officially supported components from the official documentation. There are some undocumented components, but you can check them from the source.

react-native-calendars alternatives

Like any other popular React Native package, react-native-calendars also has alternative packages, but these alternatives are not so competitive with the react-native-calendars package.

The reasons for this include:

  • Most alternative packages focus on creating date pickers — not calendar views
  • Some packages are not actively maintained and are deprecated (i.e., react-native-calendar)
  • Some packages are new and are still becoming popular within the React Native community

However, it’s worth knowing about alternatives because they may become popular in the future and may offer unique features for your requirements. So, check out the following alternative libraries:

  • react-native-calendario: A minimal, modern, scrollable calendar component that supports inbuilt range selection, date marking, and extensive style customization
  • react-native-calendar-strip: A modern and customizable calendar component that displays days in one row
  • react-native-calendar-picker: A date picker component that supports extensive style customization
  • react-native-date-picker: A library that offers time, date, date-time picker components implemented in Java and Objective-C for better performance

Conclusion

We studied how to create shareable, customized calendars with the react-native-calendars library. Code reusability boosts everyone’s productivity and improves software quality, so always strive to define a shareable calendar component if you repeat the same calendar props with the same values in multiple places.

Also, always care about your apps’ UI/UX factors by using a matching color theme for the calendar component and other UI elements. This library offers the Calendar component as the main component, but check CalendarList, Agenda, and others, before selecting one for your requirement.

LogRocket: Instantly recreate issues in your React Native apps.

LogRocket is a React Native monitoring solution that helps you reproduce issues instantly, prioritize bugs, and understand performance in your React Native apps.

LogRocket also helps you increase conversion rates and product usage by showing you exactly how users are interacting with your app. LogRocket's product analytics features surface the reasons why users don't complete a particular flow or don't adopt a new feature.

Start proactively monitoring your React Native apps — .

Shalitha Suranga Programmer | Author of Neutralino.js | Technical Writer

Leave a Reply