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
UseWeekCalendar 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.