react native strip calendar
npm install react-native-strip-calendarA headless, customizable horizontal strip calendar component for React Native with virtualized scrolling and flexible styling support.
- 📅 Flexible Date Range: Define custom start and end dates
- 🎨 Headless Design: Support for both StyleSheet and className (NativeWind) styling
- ⚡ Virtualized Scrolling: Smooth performance with large date ranges using @legendapp/list
- 🌍 Internationalization: Built-in support for multiple locales via date-fns
- 📱 React Native Compatible: Works with React Native and React Native Web
- 🎯 Customizable Rendering: Custom day and header rendering functions
- 📊 Marked Dates: Support for highlighting specific dates
- 🔄 Navigation Controls: Previous/Next week navigation with boundary checks
- 🧩 Compound Components: Modular design with Header, Week, Day, and Navigation components
- 📏 Flexible Layout: Support for custom spacing with columnGap prop
``bash`
npm install react-native-strip-calendaror
yarn add react-native-strip-calendaror
pnpm add react-native-strip-calendar
This library requires the following peer dependencies:
`bash`
npm install react react-native react-native-web
`tsx
import { StripCalendar } from 'react-native-strip-calendar';
export default function MyComponent() {
return (
endDate={new Date('2025-12-31')}
selectedDate={format(new Date(), 'yyyy-MM-dd')}
onDateChange={(date) => console.log('Selected date:', date)}
>
{(dateString) =>
{({ disabled }) => }
{({ disabled }) => }
);
}
`
`tsx`
endDate={new Date('2025-12-31')}
selectedDate={selectedDate}
onDateChange={setSelectedDate}
markedDates={['2025-01-15', '2025-02-14', '2025-03-08']}
containerHeight={120}
dayWidth={48}
>
{(dateString) => (
{format(new Date(dateString), 'MMMM yyyy')}
)}
dayProps={{
styles: {
base: {
container: {
width: 48,
height: 60,
alignItems: 'center',
justifyContent: 'center',
borderRadius: 20,
backgroundColor: '#f0f0f0',
marginHorizontal: 1,
marginVertical: 1,
},
dayName: {
fontSize: 9,
color: '#6b7280',
fontWeight: '500',
marginBottom: 2,
},
dayNumber: {
fontSize: 16,
color: '#374151',
fontWeight: '600',
},
},
today: {
container: {
backgroundColor: '#dbeafe',
borderWidth: 2,
borderColor: '#3b82f6',
},
},
selected: {
container: {
backgroundColor: '#3b82f6',
},
dayNumber: {
color: '#ffffff',
},
},
},
}}
/>
{({ disabled }) => (
disabled={disabled}
>
)}
{({ disabled }) => (
disabled={disabled}
>
)}
`tsx`
endDate={new Date('2025-12-31')}
selectedDate={selectedDate}
onDateChange={setSelectedDate}
markedDates={['2025-01-15', '2025-02-14', '2025-03-08']}
containerHeight={120}
dayWidth={48}
>
{(dateString) => (
{format(new Date(dateString), 'MMMM yyyy')}
)}
dayProps={{
renderDay: ({ date, isMarked, dayName, dayNumber, onPress }) => (
onPress={onPress}
>
{isMarked &&
),
}}
/>
{({ disabled }) => (
disabled={disabled}
>
)}
{({ disabled }) => (
disabled={disabled}
>
)}
| Prop | Type | Default | Description |
| ----------------- | ------------------------ | ----------- | ---------------------------------------------- |
| firstDay | Day | 1 | First day of the week (0 = Sunday, 1 = Monday) |startDate
| | Date | undefined | Start date of the calendar range |endDate
| | Date | undefined | End date of the calendar range |minDate
| | Date | undefined | Minimum selectable date |maxDate
| | Date | undefined | Maximum selectable date |selectedDate
| | string | undefined | Currently selected date (YYYY-MM-DD format) |onDateChange
| | (date: string) => void | undefined | Callback when date is selected |containerHeight
| | number | 60 | Height of the calendar container |dayWidth
| | number | 48 | Width of each day item |markedDates
| | string[] | [] | Array of dates to mark (YYYY-MM-DD format) |classNames
| | string | undefined | CSS class names for styling (NativeWind) |styles
| | StyleProp | undefined | StyleSheet styles for styling |locale
| | Locale | enUS | Locale for date formatting |
| Prop | Type | Default | Description |
| ------------ | ---------- | ------- | ---------------------------------------------------------- |
| dayProps | DayProps | - | Props for the Day component |className
| | object | - | CSS class names for container and week |style
| | object | - | StyleSheet styles for container and week |columnGap
| | number | 12 | Gap between week columns |weekHeight
| | number | - | Height of the week container (overrides containerHeight) |
Note: When renderDay is provided in dayProps, other properties on dayProps (like styles and classNames) are ignored. Use either dayProps for styling the default Day component or renderDay for completely custom day rendering.
| Prop | Type | Description |
| -------------- | ---------------------- | --------------------------------------------- |
| date | CalendarDate | The date object to render |classNames
| | DayStateClassNames | CSS class names for different day states |styles
| | DayStateStyles | StyleSheet styles for different day states |formatString
| | object | Custom format strings for day name and number |renderDay
| | (props) => ReactNode | Custom day renderer function |
| Prop | Type | Description |
| ----------- | -------------- | -------------------------------- |
| date | CalendarDate | The date object to render |isSelected
| | boolean | Whether the date is selected |isDisabled
| | boolean | Whether the date is disabled |isMarked
| | boolean | Whether the date is marked |dayName
| | string | Formatted day name (e.g., "Mon") |dayNumber
| | number | Day number (1-31) |onPress
| | () => void | Callback when day is pressed |
Renders the calendar header with the selected date.
`tsx`
{(dateString) => (
{format(new Date(dateString), 'MMMM yyyy')}
)}
Renders the week view with days. Supports two approaches:
#### Using dayProps (Default Day Component)
`tsx`
weekHeight={120}
className={{
container: 'list-container',
week: 'week-item',
}}
style={{
container: { padding: 16 },
week: { marginBottom: 8 },
}}
dayProps={{
styles: {
base: {
container: styles.dayContainer,
dayName: styles.dayName,
dayNumber: styles.dayNumber,
},
today: {
container: styles.todayContainer,
},
selected: {
container: styles.selectedContainer,
},
},
}}
/>
#### Using renderDay (Custom Day Component)
`tsx`
weekHeight={120}
className={{
container: 'list-container',
week: 'week-item',
}}
style={{
container: { padding: 16 },
week: { marginBottom: 8 },
}}
dayProps={{
renderDay: ({ date, isMarked, dayName, dayNumber, onPress }) => (
onPress={onPress}
>
{isMarked &&
),
}}
/>
Renders a single day (useful for custom layouts).
`tsx`
styles={customDayStyles}
classNames={customDayClassNames}
/>
Renders a button to navigate to the previous week.
`tsx`
{({ disabled }) => (
)}
Renders a button to navigate to the next week.
`tsx`
{({ disabled }) => (
)}
Renders a button to navigate to today's date.
`tsx`
`tsx
import { StyleSheet } from 'react-native';
dayProps={{
styles: {
base: {
container: styles.dayContainer,
dayName: styles.dayName,
dayNumber: styles.dayNumber,
},
today: {
container: styles.todayContainer,
},
selected: {
container: styles.selectedContainer,
},
},
}}
/>
const styles = StyleSheet.create({
dayContainer: {
width: 48,
height: 60,
alignItems: 'center',
justifyContent: 'center',
borderRadius: 20,
backgroundColor: '#f0f0f0',
marginHorizontal: 1,
marginVertical: 1,
},
dayName: {
fontSize: 9,
color: '#6b7280',
fontWeight: '500',
marginBottom: 2,
},
dayNumber: {
fontSize: 16,
color: '#374151',
fontWeight: '600',
},
todayContainer: {
backgroundColor: '#dbeafe',
borderWidth: 2,
borderColor: '#3b82f6',
},
selectedContainer: {
backgroundColor: '#3b82f6',
},
});
`
`tsx`
dayProps={{
classNames: {
base: {
container:
'w-12 h-15 items-center justify-center rounded-xl bg-gray-100 mx-0.5 my-0.5',
dayName: 'text-xs text-gray-500 font-medium mb-0.5',
dayNumber: 'text-base text-gray-700 font-semibold',
},
today: {
container: 'bg-blue-100 border-2 border-blue-500',
},
selected: {
container: 'bg-blue-500',
dayNumber: 'text-white',
},
},
}}
/>
`tsx`
dayProps={{
renderDay: ({ date, isMarked, dayName, dayNumber, onPress }) => (
onPress={onPress}
>
{isMarked &&
),
}}
/>
A custom hook that provides calendar state and navigation logic:
`tsx
import { useHorizontalCalendar } from 'react-native-strip-calendar';
const {
weeksData,
selectedDate,
currentScrollIndex,
initialScrollIndex,
canGoNext,
canGoPrevious,
handleDateSelect,
goToNextWeek,
goToPreviousWeek,
goToToday,
scrollToWeek,
} = useHorizontalCalendar({
firstDay: 1,
startDate: new Date('2025-01-01'),
endDate: new Date('2025-12-31'),
minDate: new Date('2025-01-01'),
maxDate: new Date('2025-12-31'),
selectedDate: '2025-10-18',
onDateChange: (date) => console.log(date),
});
`
`tsx
import { ko } from 'date-fns/locale';
firstDay={1} // Monday
/>;
`
See the example directory for a complete working example.
`bashInstall dependencies
pnpm install
MIT
Contributions are welcome! Please feel free to submit a Pull Request.