Timeline
Timeline is a calendar's day view, or up to a week of days side by side: a grid of hours with each event placed by its time. Events that overlap share the width, side by side, and one widens into the columns free beside it. All-day events sit in a row over the grid, a line marks the current time across today, and a tap on an empty part of the grid hands you the hour-long slot it points at, to add an event there.
It follows the design of each platform's own calendar:
- iOS, like Calendar: events tinted with their color and a bar down their leading edge, the current time in a red pill beside the hours, and the weekday and date on one line.
- Material, like Google Calendar on Android: events filled with their color and text that reads on it, a dot where the now line starts, and the weekday over the date.
- Web, like Google Calendar in a browser: filled events, the dot, and the weekday in capitals over the date.
import { Timeline } from '@elik56a/react-native-pickers';
<Timeline events={EVENTS} style={{ height: 480 }} />
Give it a height, or flex: 1 in a screen: the hours scroll inside it.
Try the propsβ
numberOfDaysformathourHeightstartHour<Timeline events={events} />
Eventsβ
An event is a plain object with an id, a title, a start and an end, plus an optional color, subtitle and allDay. Extend it with your own fields; onEventPress, renderEvent and the other callbacks give your type back.
import type { ScheduleEvent } from '@elik56a/react-native-pickers';
interface Booking extends ScheduleEvent {
customer: string;
}
<Timeline<Booking> events={bookings} onEventPress={(booking) => openBooking(booking.customer)} />;
- Overlaps are laid out as calendar apps do: each event takes the first column free when it starts, events that overlap share the width, and an event widens into the columns to its right that nothing it overlaps sits in.
- Short events still draw tall enough for their title, and count as overlapping what that height covers.
- Narrow events, under a third of the grid's width (several overlapping in a three-day view), show their title on one line, without the time.
- Over midnight, an event shows on each day it's on, cut at midnight.
- All-day events (
allDay: true) cover every day fromstarttoend, both included, in the row over the grid. - Colors are hex, such as
'#2E7D32'; the default is the theme'sprimary. iOS tints the color and Material and the web fill with it, with dark or light text, whichever reads.
Days, weeks and pagingβ
numberOfDays shows 1 to 7 days side by side, from date. A swipe sideways moves by that many days and calls onDateChange; setting date yourself moves it without calling it. With more than one day, the dates sit over the columns; onDayPress makes each a button, to open that day on its own.
Pair it with a WeekStrip or an ExpandableCalendar for a full day view, the strip picking the day and the timeline following it:
<View style={{ height: 560 }}> <WeekStrip value={day} onChange={setDay} markedDates={MARKS} /> <Timeline events={EVENTS} date={day} onDateChange={setDay} style={{ flex: 1 }} /> </View>
Hoursβ
startHour and endHour draw part of the day, such as a clinic's 7 to 20; events outside are cut to it. hourHeight sets how tall an hour is, 60 by default. The grid opens scrolled an hour before now when today is shown, else to the first event, else to 8 in the morning; initialScrollHour picks the hour yourself. format writes the hours on a '12h' or a '24h' clock, or from the locale with 'auto'.
Adding eventsβ
onSlotPress fires when an empty part of the grid is tapped, with an hour-long { start, end } starting where it was tapped, snapped down to snapMinutes (30 by default). Open your form with it, such as a TimeRangePickerField to adjust the times:
<Timeline events={events} onSlotPress={({ start, end }) => openNewEvent({ start, end })} />
Custom eventsβ
renderEvent draws the inside of each block. It gets the event, its start and end on that day, allDay, the timeText ("9:00 AM β 10:00 AM"), its colors and the theme; the block keeps its place, its press and its screen-reader label.
<Timeline
events={events}
renderEvent={({ event, colors }) => (
<View style={{ flex: 1, padding: 4, borderRadius: 6, backgroundColor: colors.background }}>
<Text style={{ color: colors.text, fontWeight: '600' }}>{event.title}</Text>
</View>
)}
/>
renderDayHeader draws each date over the columns the same way, from { date, today, theme }.
Propsβ
| Prop | Type | Default | Description |
|---|---|---|---|
events | T extends ScheduleEvent[] | β | The events to draw. |
date | Date | β | The first day shown (controlled). |
defaultDate | Date | today | The first day shown at first, when uncontrolled. |
onDateChange | (date: Date) => void | β | Fires with the new first day when the user swipes. |
numberOfDays | 1β7 | 1 | Days side by side. |
startHour | 0β23 | 0 | The first hour drawn. |
endHour | 1β24 | 24 | The hour the grid ends at. |
hourHeight | number | 60 | Height of one hour. |
initialScrollHour | number | an hour before now, else the first event, else 8 | The hour scrolled to the top at first. |
format | 'auto' | '12h' | '24h' | 'auto' | The clock the hours and times are written on. |
locale | string | device | Weekday names and times. |
showNowIndicator | boolean | true | A line at the current time across today. |
showDayHeader | boolean | true with more than one day | The weekday and date over each day. |
enableSwipe | boolean | true | Swipe sideways to the next or previous days. |
onEventPress | (event: T) => void | β | An event was tapped. |
onEventLongPress | (event: T) => void | β | An event was held. |
onSlotPress | (slot: { start: Date; end: Date }) => void | β | An empty part of the grid was tapped. |
snapMinutes | number | 30 | What a tapped slot's start snaps down to. |
onDayPress | (date: Date) => void | β | A date in the header was tapped. |
renderEvent | (info: ScheduleEventInfo<T>) => ReactNode | β | Fully custom event block. |
renderDayHeader | (info: ScheduleDayInfo) => ReactNode | β | Fully custom date over a day. |
labels | Partial<{ allDay: string; noEvents: string }> | English | The words shown, for other languages. |
style | StyleProp<ViewStyle> | β | Container style. Give it a height or flex: 1. |
styles | SlotStyles<TimelineSlot> | β | container, dayHeader, dayHeaderText, allDayRow, hourLabel, hourLine, dayColumn, event, eventTitle, eventText, nowLine. |
theme | DeepPartial<PickerTheme> | β | Theme override. |
design | 'ios' | 'material' | 'web' | from PickersProvider | The look to follow, over the provider's: iOS, Material or web. |
testID | string | β | Test ID. Each day's column gets testID.YYYY-MM-DD. |
Accessibilityβ
Each event is a button read as its title and time, such as "Standup, 9:00 AM β 9:30 AM". The dates in the header read in full, such as "Wednesday, November 11, 2026", as headers, or as buttons with onDayPress. The hour labels are hidden from screen readers, which read the times on the events instead. Tapping the grid to add needs a place on screen, so give screen-reader users an Add button too.
Relatedβ
- Agenda: the same events as a list, day by day
- WeekStrip and ExpandableCalendar: the day picker to put over it
- Migrating from react-native-calendars: its
Timeline