@symbiot.dev/react-native-timegrid
Version:
A lightweight and customizable time grid calendar component for React Native, Web, and Expo.
533 lines (402 loc) โข 26.3 kB
Markdown
<div align="center">
<h1>React Native TimeGrid ๐๏ธ ๐</h1>
</div>
> A powerful and flexible time grid and calendar component for **React Native**, designed to work seamlessly across **iOS**, **Android**, **Web**, **Expo**, and **Expo Go**. Built for performance and customization, react-native-timegrid enables developers to implement rich, interactive calendar views for a wide range of scheduling and planning needs.
<div align="center">
<img alt="NPM downloads" src="https://img.shields.io/npm/dw/@symbiot.dev/react-native-timegrid?logo=npm&label=npm&cacheSeconds=3600"/>
<img alt="NPM version" src="https://img.shields.io/npm/v/@symbiot.dev/react-native-timegrid?logo=npm&label=npm&cacheSeconds=3600"/>
<img alt="Bundle size" src="https://img.shields.io/bundlephobia/minzip/@symbiot.dev/react-native-timegrid?label=size&cacheSeconds=3600"/>
<img alt="Typescript" src="https://img.shields.io/static/v1?label=&message=TS&color=blue"/>
</div>
---
#### ๐ฑ Demo
<img alt="Demo" src="https://github.com/symbiotdev/react-native-timegrid/blob/main/demo.gif" width="300px" height="600px" />
---
## [โจ **Key Features**](#features)
๐ **Time-Based Grid Layout** โ Intuitive hourly/daily layout ideal for agenda, schedule, or shift views.
๐จ **Fully Customizable** โ Modify headers, time slots, event appearance, and grid styling to fit any design system.
๐ง **Lightweight** โ Minimal dependencies and optimized bundle size for faster load times and efficient performance.
๐ **Cross-Platform Compatibility** โ Works out of the box with:
- **React Native CLI**
- **Expo**
- **Expo Go**
- **React Native Web** for browser support
โ๏ธ **Optimized Performance** โ Supports virtual scrolling and large datasets with smooth interactions.
โ **Interactive Features** โ Tap, drag, and dynamic updates for real-time scheduling apps.
๐ฑ **Responsive Design** โ Adapts to various screen sizes and device orientations.
๐งฉ **Modular Integration** โ Use as a standalone calendar or integrate into more complex scheduling systems.
---
## ๐ [**Ideal For**](#for)
- ๐
Personal or team calendars
- ๐๏ธ Booking and reservation systems (salons, gyms, clinics)
- ๐ฅ Employee shift and availability planners
- ๐ Event timeline visualizations
- ๐งโ๐ซ Class schedules and academic timetables
- ๐ง Task and time management tools
- ๐ฅ Appointment scheduling for healthcare or therapy
- ๐ฆ Delivery or logistics scheduling
- ๐ผ Project planning dashboards with time allocation
- ๐ Maintenance or service job assignment trackers
- ๐ Student agendas or learning planners
- ๐ค Conference session planners and expo booth schedules
- ๐ฅ and more ...
Whether you're building a cross-platform scheduling app or integrating a responsive calendar view into an existing project, react-native-timegrid provides a modern, customizable, and performant solution for all major platforms.
---
## [๐ฉ๐ฟโ๐ป **Installation**](#installation)
```bash
npm install .dev/react-native-timegrid react-native-reanimated react-native-gesture-handler date-fns
# or
yarn add .dev/react-native-timegrid react-native-reanimated react-native-gesture-handler date-fns
# for web support
npm i react-native-web
# or
yarn add react-native-web
# to support recurring events
npm i rrule
# or
yarn add rrule
```
```tsx
import { TimeGrid } from '@symbiot.dev/react-native-timegrid';
// ... and voilร ๐ซ
<TimeGrid />
```
> Just import it and boom โ youโve got a slick, cross-platform calendar view.
> Fast setup, full control, and zero headaches. ๐ช๐ป๐ฅ
---
## ๐จ `TimeGridThemeProps` โ [Theming & Appearance](#theme)
Customize the look and feel of the `TimeGrid` with flexible styling options. You can define global styles, override them based on theme (`light` or `dark`), or combine both approaches.
### ๐งฉ Structure
The `TimeGridThemeProps` type supports three layers:
1. **Global Styling (applies to all schemes unless overridden)**
2. **Per-scheme Overrides** โ Use `light` and `dark` keys to define specific values
3. **Theme Scheme Selector** โ Control active theme using `scheme`
```ts
type TimeGridThemeProps = TimeGridStyling & {
scheme?: 'light' | 'dark'; // system default
light?: TimeGridStyling;
dark?: TimeGridStyling;
};
```
### ๐งฑ Available Styling Props
| Prop Name | Description | iOS | Android | Web | Default Value |
|---------------------------------------|----------------------------------------------------|:---:|:-------:|:---:|---------------------------------------|
| `backgroundColor` | Main background color of the grid view | โ
| โ
| โ
| light: `#FFFFFF`, dark: `#000000` |
| `dayHeaderBackgroundColor` | Background for the day-of-week header bar | โ
| โ
| โ
| light: `#FFFFFF`, dark: `#000000` |
| `dayHeaderTextColor` | Text color for day-of-week labels | โ
| โ
| โ
| light: `#000000`, dark: `#FFFFFF` |
| `headerSafeAreaBackgroundColor` | Background for top safe area (e.g., notch spacing) | โ
| โ
| โ
| light: `#FFFFFF`, dark: `#000000` |
| `allDayEventsSafeAreaBackgroundColor` | Background for all-day events safe area | โ
| โ
| โ
| light: `#FFFFFF`, dark: `#000000` |
| `allDayEventsSafeAreaTextColor` | Text color for all-day events safe area | โ
| โ
| โ
| light: `#000000`, dark: `#FFFFFF` |
| `timelineBackgroundColor` | Background color for timeline column | โ
| โ
| โ
| light: `#FFFFFF`, dark: `#000000` |
| `timelineTextColor` | Text color for hour/time labels | โ
| โ
| โ
| light: `#A9A9A9`, dark: `#A9A9A9` |
| `verticalLineColor` | Color of vertical time slot dividers | โ
| โ
| โ
| light: `#F1F1F1`, dark: `#303030` |
| `horizontalLineColor` | Color of horizontal grid lines | โ
| โ
| โ
| light: `#F1F1F1`, dark: `#303030` |
| `nowIndicatorColor` | Color of the โnowโ line showing current time | โ
| โ
| โ
| `#FF6F61` |
| `unavailableTimeBackgroundColor` | Color for visually marking unavailable time blocks | โ
| โ
| โ
| light: `#F1F1F170`, dark: `#30303070` |
| `dropZoneBackgroundColor` | Background for areas that accept dragged events | โ
| โ
| โ
| `transparent` |
| `eventBackgroundColor` | Default background for event blocks | โ
| โ
| โ
| light: `#D8E4F1`, dark: `#475867` |
| `eventTextColor` | Text color used in event blocks | โ
| โ
| โ
| light: `#000000`, dark: `#FFFFFF` |
| `eventBorderColor` | Border color for events | โ
| โ
| โ
| light: `#FFFFFF`, dark: `#000000` |
```tsx
<TimeGrid
//...
theme={{
backgroundColor: '#f0f0f0', // for light and dark mode
}}
/>
// ----
<TimeGrid
//...
theme={{
backgroundColor: '#f0f0f0', // only applies to light mode
eventTextColor: '#323232', // only applies to dark mode
dark: {
backgroundColor: '#020202',
},
light: {
eventTextColor: '#505050',
}
}}
/>
```
---
## ๐ `TimeGridDimensionsProps` โ [Layout & Dimensions](#dimensions)
Control the physical dimensions of key areas inside the `TimeGrid`. All values are in **pixels** and optional โ sensible defaults are provided.
### ๐งฑ Available Dimension Props
| Prop Name | Description | iOS | Android | Web | Default Value |
|-----------------------|-------------------------------------------------------|:---:|:-------:|:---:|---------------|
| `width` | TimeGrid width | โ
| โ
| โ
| `system` |
| `dayHeaderHeight` | Height of the day header (weekday labels) | โ
| โ
| โ
| `50` |
| `allDayEventHeight` | Height for all-day events | โ
| โ
| โ
| `25` |
| `timelineWidth` | Width of the left-side timeline area | โ
| โ
| โ
| `58` |
| `horizontalLineSize` | Height of horizontal divider lines | โ
| โ
| โ
| `0.5` |
| `nowIndicatorHeight` | Thickness of the โnowโ line | โ
| โ
| โ
| `0.5` |
| `gridVerticalPadding` | Defines the vertical spacing (top and bottom padding) | โ
| โ
| โ
| `0` |
| `eventBorderWidth` | Width of the border around an event element | โ
| โ
| โ
| `1` |
| `eventBorderRadius` | Radius of the border corners for an event element | โ
| โ
| โ
| `5` |
```tsx
<TimeGrid
//...
timelineWidth={60}
/>
```
___
## ๐
`TimeGridDatetimeProps` โ [Date & Time Configuration](#datetime)
Set the visible time range, number of days, start date, timezone, and other calendar-related behaviors.
These props give you precise control over how the `TimeGrid` renders days and time slots.
### ๐งฑ Available Datetime Props
| Prop Name | Description | iOS | Android | Web | Default Value |
|----------------|--------------------------------------------------------------------------|:---:|:-------:|:---:|---------------|
| `startDate` | The starting datetime for the visible grid (defaults to current date) | โ
| โ
| โ
| `new Date()` |
| `weekStartsOn` | Day the week starts on (0=Sun, 1=Mon, etc.) | โ
| โ
| โ
| `0` (Sunday) |
| `hiddenDays` | Days of the week to hide (e.g., `[0, 6]` to hide Sun & Sat) | โ
| โ
| โ
| `[]` |
| `numberOfDays` | Number of days to render in the view | โ
| โ
| โ
| `1` |
| `timeFrom` | Start of the visible time range (in **minutes**, e.g., `480` = 8:00 AM) | โ
| โ
| โ
| `0` |
| `timeTo` | End of the visible time range (in **minutes**, e.g., `1200` = 8:00 PM) | โ
| โ
| โ
| `1440` |
| `timeInterval` | Minutes between each horizontal time line | โ
| โ
| โ
| `60` |
| `timeStep` | Time rounding when placing events (in minutes) | โ
| โ
| โ
| `15` |
| `minDate` | Earliest date to allow navigation | โ
| โ
| โ
| `โ` |
| `maxDate` | Latest date to allow navigation | โ
| โ
| โ
| `โ` |
| `timezone` | Timezone name (e.g., `'America/New_York', '+02:00'`) for offset handling | โ
| โ
| โ
| `local` |
```tsx
<TimeGrid
//...
weekStartsOn={1} // starts on Monday
numberOfDays={3}
hiddenDays={[0, 6]} // exclude weekends (Sun,Sut)
/>
```
___
## ๐๏ธ `TimeGridEventsProps` โ [Events, Rendering & Recurrence](#events)
Configure how events appear and behave inside the calendar. Supports custom colors, recurring events, readonly mode, and timezone-specific scheduling.
> ๐ฆ๐จ **Overlapping Events**
> Events that share time ranges are **fully supported out-of-the-box**.
> The grid will automatically stack or split them side-by-side depending on layout space.
### ๐งฑ Available Event Props
| Prop Name | Description | iOS | Android | Web | Default Value |
|----------------------------|--------------------------------------------------------------------|:---:|:-------:|:---:|---------------|
| `createEventDuration` | Duration (in **minutes**) to assign when user creates a new event. | โ
| โ
| โ
| `60` |
| `allDayEventsSafeAreaText` | Placeholder for all-day events safe area | โ
| โ
| โ
| `All-day` |
| `events` | Array of `TimeGridEvent` to render inside the time grid | โ
| โ
| โ
| `[]` |
### `TimeGridEvent`
| Field Name | Description | Type | Required | Default |
|-------------------|-------------------------------------------------|----------------------------|----------|--------------|
| `id` | Unique identifier for the event | `string` | โ
| โ |
| `start` | Start datetime of the event | `Date \| string \| number` | โ
| โ |
| `end` | End datetime of the event | `Date \| string \| number` | โ
| โ |
| `text` | Event title or description | `string` | optional | `datetime` |
| `backgroundColor` | Override default event background color | `string` | optional | `theme` |
| `textColor` | Override text color inside the event | `string` | optional | `theme` |
| `timezone` | IANA timezone name for localized event handling | `string` | optional | `grid-level` |
| `readonly` | Prevent this event from being dragged | `boolean` | optional | `false` |
| `rrule` | RRULE string (e.g., `'RRULE:FREQ=DAILY'`) | `string` | optional | โ |
> โน๏ธ **Need help creating a recurrence rule?**
> Use the visual generator at [rrule.js playground](https://jkbrzt.github.io/rrule/) to quickly create and test valid RRULE strings.
```tsx
<TimeGrid
//...
createEventDuration={30} // new created events equals 30 mins
events={[
//...
{
id: 'timestamp',
start: '2025-04-25T18:00:00.500Z',
end: '2025-04-25T19:00:00.800Z',
backgroundColor: '#DDDDDD',
textColor: '#000000',
readonly: true // prevent from being dragged
}
]}
/>
```
___
## ๐ซ `TimeGridUnavailableTimeProps` โ [Blocking Unavailable Times](unavailable-time)
Mark specific time ranges as **unavailable** โ either for a specific date or recurring on weekdays.
These periods can block event creation, display with a distinct background and could be used with a different combinations.
```ts
export type TimeGridUnavailableTime = {
from: number; // in minutes (e.g. 480 = 8:00 AM)
to: number; // in minutes (e.g. 1020 = 5:00 PM)
start?: Date | string;
end?: Date | string;
} & (
| {
weekday?: Day; // 0 (Sunday) to 6 (Saturday)
date?: never;
}
| {
date?: Date | string;
weekday?: never;
}
);
export type TimeGridUnavailableTimeProps = {
unavailableTime?: TimeGridUnavailableTime[];
};
```
### ๐งฑ Available Unavailable Time Props
| Prop Name | Description | iOS | Android | Web | Default Value |
|-------------------|--------------------------------------------------------------------------------------------------------------|:---:|:-------:|:---:|---------------|
| `unavailableTime` | Array of time rules (by weekday or specific date). Uses `from`/`to` in **minutes**; Priority: date, weekday; | โ
| โ
| โ
| `[]` |
```tsx
<TimeGrid
//...
unavailableTime={[
{ from: 0, to: 480 } // everyday 00:00 (12:00 AM) -> 08:00 AM
]}
/>
// ----
<TimeGrid
//...
unavailableTime={[
{ from: 0, to: 480, start: '2025-04-25T18:00:00.500Z' } // daily, starting April 25
]}
/>
// ----
<TimeGrid
//...
unavailableTime={[
{ from: 0, to: 480, start: '2025-04-25T00:00:00.500Z', end: '2025-04-26T00:00:00.500Z' } // only 2 days - April 25 and 26
]}
/>
// ----
// Priority behaviour
<TimeGrid
//...
unavailableTime={[
{ from: 0, to: 480 }, // daily
{ from: 1020, to: 1440, weekday: 1 }, // ignore the first element of the array and apply every Monday
{ from: 480, to: 900, date: '2025-04-28T18:00:00.500Z' } // ignore the first and second rules for Monday, April 28th
]}
/>
// ----
// Save behaviour
<TimeGrid
//...
unavailableTime={[
{ from: 0, to: 480 }, // daily
{ from: 0, to: 480, date: '2025-04-28T18:00:00.500Z' }, // save the daily rule for April 28
{ from: 480, to: 900, date: '2025-04-28T18:00:00.500Z' } // ignore the first and second rules for Monday, April 28th
]}
/>
```
___
## ๐ฎ `TimeGridControllerProps` โ [Interaction & Display Toggles](#controller)
Control user interaction features like swiping, dragging, snapping, and visibility of UI components like the timeline, all-day events etc.
### ๐งฑ Available Controller Props
| Prop Name | Description | iOS | Android | Web | Default Value |
|-------------------------|----------------------------------------------------------------|:---:|:-------:|:---:|---------------|
| `lazy` | Enable to improve performance on the slow devices | โ
| โ
| โ
| `true` |
| `swipeable` | Enable swiping between days or weeks | โ
| โ
| โ
| `false` |
| `snappable` | Snap scroll/drag to the nearest date | โ
| โ
| โ
| `false` |
| `draggable` | Allow dragging events to create/reschedule them | โ
| โ
| โ
| `true` |
| `debounceable` | Delays date change handler to avoid rapid successive triggers. | โ
| โ
| โ
| `false` |
| `rtl` | Enable right-to-left layout (for RTL languages) | โ
| โ
| โ
| `false` |
| `isDayHeaderVisible` | Toggle visibility of the top day-of-week header | โ
| โ
| โ
| `true` |
| `isTimelineVisible` | Toggle visibility of the left-side timeline | โ
| โ
| โ
| `true` |
| `isNowIndicatorVisible` | Show/hide the "now" indicator line | โ
| โ
| โ
| `true` |
| `isAllDayEventsEnabled` | Enable support for all-day events | โ
| โ
| โ
| `true` |
| `isAllDayEventsVisible` | Toggle visibility of the all-day events | โ
| โ
| โ
| `true` |
```tsx
<TimeGrid
//...
swipeable // swipe actions turned on
draggable // user can drag events
snappable // date snapping enabled
/>
```
___
## โฑ `TimeGridFormatterProps` โ [Text Formatting](#formatter)
Customize how dates and times appear within the TimeGrid component using format strings (e.g., `dd`, `HH:mm`, `EEE`).
These follow [date-fns](https://date-fns.org/docs/format) formatting syntax.
### ๐งฑ Available Formatter Props
| Prop Name | Description | iOS | Android | Web | Default Value |
|-----------------------|----------------------------------------------------------------|:---:|:-------:|:---:|---------------|
| `dayHeaderTextFormat` | Format for the day header labels (e.g., `Mon`, `Apr 20`) | โ
| โ
| โ
| `EEEEEE dd` |
| `timelineTextFormat` | Format for the left-side time labels (e.g., `08:00`) | โ
| โ
| โ
| `p` |
| `locale` | Locale object from `date-fns` for internationalized formatting | โ
| โ
| โ
| `en` |
```tsx
import { zhCN } from 'date-fns/locale/zh-CN';
<TimeGrid
//...
locale={zhCN} // Chinese localization
/>
```
___
## ๐ฏ `TimeGridActionsProps` โ [User Interaction Callbacks](#actions)
Listen and respond to interactions like taps, long presses, event edits, and grid context menus.
### ๐งฑ Available Action Callbacks
| Prop Name | Description | iOS | Android | Web |
|-------------------------------|------------------------------------------------------------------------------------------------|:---:|:-------:|:---:|
| `onChangeDate` | Called when the grid's date range changes | โ
| โ
| โ
|
| `onHeaderSafeAreaPress` | Triggered when the top header area is tapped | โ
| โ
| โ
|
| `onAllDayEventsSafeAreaPress` | Triggered when the all-day events header area is tapped | โ
| โ
| โ
|
| `onDayHeaderPress` | Called when a specific day header is pressed | โ
| โ
| โ
|
| `onLoad` | Called once the grid is initialized and ready | โ
| โ
| โ
|
| `onUnavailableTimePress` | Fired on tap inside an unavailable time block | โ
| โ
| โ
|
| `onUnavailableTimeLongPress` | Fired on long press inside an unavailable time block | โ
| โ
| โ
|
| `onDatetimeContextMenu` | Right-click/context menu trigger at specific date/time (with x/y coords) | ๐ซ | ๐ซ | โ
|
| `onDatetimePress` | Triggered on a tap inside an available time cell | โ
| โ
| โ
|
| `onDatetimeLongPress` | Triggered on long press inside an available time cell | โ
| โ
| โ
|
| `onEventPress` | Called when an event is tapped | โ
| โ
| โ
|
| `onEventLongPress` | Called when an event is long-pressed | โ
| โ
| โ
|
| `onEventUpdated` | Triggered when an event is changed (drag); return `false` to keep updating, `reject` to cancel | โ
| โ
| โ
|
| `onEventCreated` | Triggered when a new event is created by the user | โ
| โ
| โ
|
```tsx
import { useState } from 'react';
const [selectedDate, setSelectedDate] = useState(new Date());
<TimeGrid
//...
onChangeDate={setSelectedDate} // selectedDate updated
/>
```
___
## ๐ง `TimeGridRef` โ [Imperative Control API](#ref)
Control and interact with the TimeGrid instance programmatically using a React ref.
Great for custom toolbars, buttons, or dynamic navigation.
### ๐งฑ Available Ref Methods
| Method | Description | Returns |
|-----------------|---------------------------------------------------------------------------------|---------------------|
| `getPrevDate()` | Returns the previous valid date range (optionally ignoring `minDate`/`maxDate`) | `Date \| undefined` |
| `getNextDate()` | Returns the next valid date range (optionally ignoring `minDate`/`maxDate`) | `Date \| undefined` |
| `prevDate()` | Navigate to the previous available date | `void` |
| `nextDate()` | Navigate to the next available date | `void` |
| `back()` | Navigate backward through a specified number of days (`numberOfDays`) | `void` |
| `forward()` | Navigate forward through a specified number of days (`numberOfDays`) | `void` |
| `toDatetime()` | Scroll or jump to a specific date/time | `void` |
| `createEvent()` | Programmatically create a new event at a given datetime | `void` |
```tsx
import { useRef } from 'react';
import { TimeGrid, TimeGridRef } from '.dev/react-native-timegrid';
const timeGridRef = useRef<TimeGridRef>(null);
<TimeGrid
//...
ref={timeGridRef}
onDatetimeLongPress={(params) => timeGridRef.current?.createEvent(params)}
/>
```
___
### [๐ **Platform Support**](#support)
- โ
iOS
- โ
Android
- โ
Web
- โ
Expo & Expo Go
---
### [๐ฆ **Bundle Size**](#size)
Lightweight and fast โ see actual size on [Bundlephobia](https://bundlephobia.com/package/@symbiot.dev/react-native-timegrid).
---
### [๐ฌ Contributing](#contributing)
Contributions welcome! Feel free to open issues, discussions, or suggestions.
---
### [๐ Who's Using This?](#usage)
Using `react-native-timegrid` in your app or product?
Feel free to share your project โ it might be featured in this section!
> โ๏ธ Open pull request, or reach out directly to get included.
___
### [๐ Got Ideas or Gaps to Fill?](#ideas)
Know of existing libraries or functionality that could be improved?
Have an idea for something completely new?
**Symbiot** is ready to take on the challenge โ feedback, feature requests, or collaboration ideas are always welcome!
> ๐ง Letโs build better tools together. Start the conversation or contact via [email](mailto:info.dev).
___
### [๐งพ License](#license)
MIT โ Made with โค๏ธ by [Symbiot](https://symbiot.dev)