Skip to main content
Version: v1.0.0

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';
Loading…
<Timeline events={EVENTS} style={{ height: 480 }} />

Give it a height, or flex: 1 in a screen: the hours scroll inside it.

Try the props​

Platform
numberOfDays
format
hourHeight
startHour
<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 from start to end, both included, in the row over the grid.
  • Colors are hex, such as '#2E7D32'; the default is the theme's primary. 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:

Loading…
<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​

PropTypeDefaultDescription
eventsT extends ScheduleEvent[]β€”The events to draw.
dateDateβ€”The first day shown (controlled).
defaultDateDatetodayThe first day shown at first, when uncontrolled.
onDateChange(date: Date) => voidβ€”Fires with the new first day when the user swipes.
numberOfDays1–71Days side by side.
startHour0–230The first hour drawn.
endHour1–2424The hour the grid ends at.
hourHeightnumber60Height of one hour.
initialScrollHournumberan hour before now, else the first event, else 8The hour scrolled to the top at first.
format'auto' | '12h' | '24h''auto'The clock the hours and times are written on.
localestringdeviceWeekday names and times.
showNowIndicatorbooleantrueA line at the current time across today.
showDayHeaderbooleantrue with more than one dayThe weekday and date over each day.
enableSwipebooleantrueSwipe 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.
snapMinutesnumber30What 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.
labelsPartial<{ allDay: string; noEvents: string }>EnglishThe words shown, for other languages.
styleStyleProp<ViewStyle>β€”Container style. Give it a height or flex: 1.
stylesSlotStyles<TimelineSlot>β€”container, dayHeader, dayHeaderText, allDayRow, hourLabel, hourLine, dayColumn, event, eventTitle, eventText, nowLine.
themeDeepPartial<PickerTheme>β€”Theme override.
design'ios' | 'material' | 'web'from PickersProviderThe look to follow, over the provider's: iOS, Material or web.
testIDstringβ€”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.