Skip to main content
The ExpandableCalendar component provides a collapsible calendar that can switch between month and week views. It works seamlessly with AgendaList and must be wrapped in a CalendarProvider.

Basic expandable calendar

Create a collapsible calendar with agenda list:
import React, {useCallback} from 'react';
import {StyleSheet} from 'react-native';
import {
  ExpandableCalendar,
  AgendaList,
  CalendarProvider
} from 'react-native-calendars';

const App = () => {
  const items = [
    {
      title: '2024-11-06',
      data: [
        {name: 'Morning meeting', time: '09:00'},
        {name: 'Lunch with client', time: '12:30'}
      ]
    },
    {
      title: '2024-11-07',
      data: [
        {name: 'Team standup', time: '10:00'}
      ]
    }
  ];

  const renderItem = useCallback(({item}) => {
    return (
      <View style={styles.item}>
        <Text style={styles.itemTitle}>{item.name}</Text>
        <Text style={styles.itemTime}>{item.time}</Text>
      </View>
    );
  }, []);

  return (
    <CalendarProvider date={'2024-11-06'}>
      <ExpandableCalendar
        firstDay={1}
        markedDates={{
          '2024-11-06': {marked: true, dotColor: 'blue'},
          '2024-11-07': {marked: true, dotColor: 'blue'}
        }}
      />
      <AgendaList
        sections={items}
        renderItem={renderItem}
      />
    </CalendarProvider>
  );
};

const styles = StyleSheet.create({
  item: {
    backgroundColor: 'white',
    padding: 20,
    marginVertical: 8,
    marginHorizontal: 16,
    borderRadius: 8
  },
  itemTitle: {
    fontSize: 16,
    fontWeight: 'bold'
  },
  itemTime: {
    fontSize: 14,
    color: '#666',
    marginTop: 4
  }
});

export default App;
The CalendarProvider component is required to manage state between the expandable calendar and agenda list.

Complete expandable calendar example

A production-ready implementation with custom header and theme:
import React, {useRef, useCallback} from 'react';
import {Animated, Easing, StyleSheet, Text, TouchableOpacity, View} from 'react-native';
import {
  ExpandableCalendar,
  AgendaList,
  CalendarProvider
} from 'react-native-calendars';
import type XDate from 'xdate';

const CHEVRON = require('./img/next.png');

const App = () => {
  const markedDates = {
    '2024-11-06': {marked: true, dotColor: '#50cebb'},
    '2024-11-07': {marked: true, dotColor: '#50cebb'},
    '2024-11-08': {marked: true, dotColor: '#50cebb'}
  };

  const theme = {
    backgroundColor: '#ffffff',
    calendarBackground: '#ffffff',
    selectedDayBackgroundColor: '#00adf5',
    selectedDayTextColor: '#ffffff',
    todayTextColor: '#00adf5',
    dayTextColor: '#2d4150',
    textDisabledColor: '#d9e1e8'
  };

  const todayBtnTheme = {
    todayButtonTextColor: '#00adf5'
  };

  const items = [
    {
      title: '2024-11-06',
      data: [{name: 'Item for 2024-11-06 #1'}, {name: 'Item for 2024-11-06 #2'}]
    },
    {
      title: '2024-11-07',
      data: [{name: 'Item for 2024-11-07'}]
    }
  ];

  const renderItem = useCallback(({item}) => {
    return (
      <View style={styles.item}>
        <Text>{item.name}</Text>
      </View>
    );
  }, []);

  const calendarRef = useRef(null);
  const rotation = useRef(new Animated.Value(0));

  const toggleCalendarExpansion = useCallback(() => {
    const isOpen = calendarRef.current?.toggleCalendarPosition();
    Animated.timing(rotation.current, {
      toValue: isOpen ? 1 : 0,
      duration: 200,
      useNativeDriver: true,
      easing: Easing.out(Easing.ease)
    }).start();
  }, []);

  const renderHeader = useCallback(
    (date) => {
      const rotationInDegrees = rotation.current.interpolate({
        inputRange: [0, 1],
        outputRange: ['0deg', '180deg']
      });
      return (
        <TouchableOpacity style={styles.header} onPress={toggleCalendarExpansion}>
          <Text style={styles.headerTitle}>{date?.toString('MMMM yyyy')}</Text>
          <Animated.Image
            source={CHEVRON}
            style={{transform: [{rotate: '-90deg'}, {rotate: rotationInDegrees}]}}
          />
        </TouchableOpacity>
      );
    },
    [toggleCalendarExpansion]
  );

  return (
    <CalendarProvider
      date={'2024-11-06'}
      showTodayButton
      theme={todayBtnTheme}
    >
      <ExpandableCalendar
        renderHeader={renderHeader}
        ref={calendarRef}
        theme={theme}
        firstDay={1}
        markedDates={markedDates}
      />
      <AgendaList
        sections={items}
        renderItem={renderItem}
        sectionStyle={styles.section}
      />
    </CalendarProvider>
  );
};

const styles = StyleSheet.create({
  header: {
    flexDirection: 'row',
    justifyContent: 'center',
    alignItems: 'center',
    marginVertical: 10
  },
  headerTitle: {
    fontSize: 16,
    fontWeight: 'bold',
    marginRight: 6
  },
  item: {
    backgroundColor: 'white',
    padding: 20,
    marginVertical: 8,
    marginHorizontal: 16,
    borderRadius: 8
  },
  section: {
    backgroundColor: '#f0f0f0',
    color: 'grey',
    textTransform: 'capitalize'
  }
});

export default App;
Use the toggleCalendarPosition method to programmatically expand or collapse the calendar.

Week view mode

Use WeekCalendar for a week-only view:
import React, {useCallback} from 'react';
import {View, Text, StyleSheet} from 'react-native';
import {
  WeekCalendar,
  AgendaList,
  CalendarProvider
} from 'react-native-calendars';

const App = () => {
  const markedDates = {
    '2024-11-06': {marked: true, dotColor: 'blue'},
    '2024-11-07': {marked: true, dotColor: 'blue'}
  };

  const items = [
    {
      title: '2024-11-06',
      data: [{name: 'Meeting'}, {name: 'Lunch'}]
    }
  ];

  const renderItem = useCallback(({item}) => {
    return (
      <View style={styles.item}>
        <Text>{item.name}</Text>
      </View>
    );
  }, []);

  return (
    <CalendarProvider date={'2024-11-06'}>
      <WeekCalendar
        firstDay={1}
        markedDates={markedDates}
      />
      <AgendaList
        sections={items}
        renderItem={renderItem}
      />
    </CalendarProvider>
  );
};

const styles = StyleSheet.create({
  item: {
    backgroundColor: 'white',
    padding: 20,
    marginVertical: 8,
    marginHorizontal: 16,
    borderRadius: 8
  }
});

export default App;

CalendarProvider props

<CalendarProvider
  // Initial date to display
  date={'2024-11-06'}
  
  // Show today button
  showTodayButton={true}
  
  // Callback when date changes
  onDateChanged={(date, updateSource) => {
    console.log('Date changed:', date);
  }}
  
  // Callback when month changes
  onMonthChange={(date) => {
    console.log('Month changed:', date);
  }}
/>

ExpandableCalendar props

<ExpandableCalendar
  // Initial position ('open' or 'closed')
  initialPosition={ExpandableCalendar.positions.CLOSED}
  
  // Disable pan gesture
  disablePan={false}
  
  // Hide knob
  hideKnob={false}
  
  // Disable week scroll in closed position
  disableWeekScroll={false}
  
  // Close calendar on day press
  closeOnDayPress={true}
  
  // Callback when calendar opens/closes
  onCalendarToggled={(isOpen) => {
    console.log('Calendar is', isOpen ? 'open' : 'closed');
  }}
  
  // Custom header renderer
  renderHeader={(date) => <CustomHeader date={date} />}
  
  // Enable horizontal scrolling
  horizontal={true}
  
  // All Calendar props are also supported
  firstDay={1}
  markedDates={markedDates}
  theme={theme}
/>

AgendaList props

<AgendaList
  // Section data array
  sections={[
    {title: '2024-11-06', data: [...]},
    {title: '2024-11-07', data: [...]}
  ]}
  
  // Render function for items
  renderItem={({item}) => <View />}
  
  // Section header style
  sectionStyle={styles.section}
  
  // Day format for section headers
  dayFormat={'yyyy-MM-dd'}
  
  // Scroll to next event
  scrollToNextEvent={false}
/>
The sections array in AgendaList must have title (date string) and data (array of items) properties.

Controlling calendar position

Programmatically control the calendar’s expanded/collapsed state:
import React, {useRef} from 'react';
import {Button, View} from 'react-native';
import {ExpandableCalendar, CalendarProvider} from 'react-native-calendars';

const App = () => {
  const calendarRef = useRef(null);

  const toggleCalendar = () => {
    calendarRef.current?.toggleCalendarPosition();
  };

  return (
    <CalendarProvider date={'2024-11-06'}>
      <ExpandableCalendar ref={calendarRef} />
      <Button title="Toggle Calendar" onPress={toggleCalendar} />
    </CalendarProvider>
  );
};

export default App;

Custom arrow icons

Customize navigation arrows:
import React from 'react';
import {ExpandableCalendar, CalendarProvider} from 'react-native-calendars';

const leftArrow = require('./img/previous.png');
const rightArrow = require('./img/next.png');

const App = () => {
  return (
    <CalendarProvider date={'2024-11-06'}>
      <ExpandableCalendar
        leftArrowImageSource={leftArrow}
        rightArrowImageSource={rightArrow}
        firstDay={1}
      />
    </CalendarProvider>
  );
};

export default App;

Position constants

Access the position constants:
import {ExpandableCalendar} from 'react-native-calendars';

// Available positions
ExpandableCalendar.positions.OPEN
ExpandableCalendar.positions.CLOSED

// Usage
<ExpandableCalendar
  initialPosition={ExpandableCalendar.positions.OPEN}
/>
Combine ExpandableCalendar with AgendaList for a complete scheduling UI with smooth transitions between calendar and event views.

Build docs developers (and LLMs) love