react-native-apple-sleep-chart
v1.1.0
Published
A React Native sleep stages chart (hypnogram) component inspired by the iOS Health app. Features interactive pan gesture with haptic feedback, customizable themes, and smooth Reanimated animations.
Maintainers
Readme
react-native-apple-sleep-chart
A React Native sleep stages chart component inspired by the iOS Health app. Features interactive pan gesture with cursor, haptic feedback on segment transitions, customizable themes, and smooth Reanimated animations.

Features
- iOS Health app-style sleep stages hypnogram
- Interactive pan gesture with animated cursor and info card
- Haptic feedback when crossing segment boundaries (optional)
- Fully customizable theme (colors, stage labels, stage colors)
- Dark mode support out of the box (iOS
DynamicColorIOS) - Smooth spring-based animations via
react-native-reanimated - Gradient underlay with masked view
- TypeScript-first with full type exports
onSegmentChangecallback for external integrations
Installation
# Using npm
npm install react-native-apple-sleep-chart
# Using yarn
yarn add react-native-apple-sleep-chartPeer Dependencies
This library requires the following peer dependencies. Install any you don't already have:
yarn add react-native-gesture-handler react-native-reanimated react-native-svg @react-native-masked-view/masked-viewFor haptic feedback support (optional):
yarn add react-native-haptic-feedbackThen install iOS pods:
cd ios && pod installNote: Make sure
react-native-reanimatedis properly configured in yourbabel.config.jswith the Reanimated plugin. See the Reanimated installation docs.
Quick Start
import React from "react";
import { SafeAreaView } from "react-native";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import {
SleepStagesChart,
type SleepSegment,
} from "react-native-apple-sleep-chart";
const sleepData: SleepSegment[] = [
{
id: 0,
type: "core",
from: new Date("2024-01-15T00:00:00"),
to: new Date("2024-01-15T00:45:00"),
},
{
id: 1,
type: "deep",
from: new Date("2024-01-15T00:45:00"),
to: new Date("2024-01-15T01:30:00"),
},
{
id: 2,
type: "rem",
from: new Date("2024-01-15T01:30:00"),
to: new Date("2024-01-15T02:10:00"),
},
{
id: 3,
type: "core",
from: new Date("2024-01-15T02:10:00"),
to: new Date("2024-01-15T03:00:00"),
},
{
id: 4,
type: "awake",
from: new Date("2024-01-15T03:00:00"),
to: new Date("2024-01-15T03:02:00"),
},
{
id: 5,
type: "deep",
from: new Date("2024-01-15T03:02:00"),
to: new Date("2024-01-15T04:00:00"),
},
];
export default function App() {
return (
<GestureHandlerRootView style={{ flex: 1 }}>
<SafeAreaView style={{ flex: 1 }}>
<SleepStagesChart
data={sleepData}
enableHaptics={true}
onSegmentChange={(segment) => {
console.log("Now viewing:", segment.type);
}}
/>
</SafeAreaView>
</GestureHandlerRootView>
);
}Props
SleepStagesChartProps
| Prop | Type | Default | Description |
| ------------------- | --------------------------------- | ------------ | --------------------------------------------------------------------------------------------------------------------------- |
| data | SleepSegment[] | required | Array of sleep segments to display. Must be sorted by from date ascending. |
| theme | SleepChartTheme | undefined | Optional theme overrides for colors and stage appearance. |
| enableHaptics | boolean | true | Enable haptic feedback when the cursor crosses segment boundaries. Requires react-native-haptic-feedback to be installed. |
| onSegmentChange | (segment: SleepSegment) => void | undefined | Callback fired when the cursor enters a different segment during pan gesture. |
| showHeader | boolean | true | Whether to show the header displaying total sleep time. |
| showRefreshButton | boolean | false | Whether to show the refresh button in the header. |
| onRefreshPress | () => void | undefined | Callback fired when the refresh button is pressed. Only relevant if showRefreshButton is true. |
Types
SleepSegment
Represents a single sleep segment in the chart.
type SleepSegment = {
id: number; // Unique identifier for the segment
type: SleepStageKey; // "awake" | "rem" | "core" | "deep"
from: Date; // Start time of the segment
to: Date; // End time of the segment
};SleepStageKey
type SleepStageKey = "awake" | "rem" | "core" | "deep";SleepChartTheme
Optional theme overrides. All fields are optional -- only override what you need.
type SleepChartTheme = {
colors?: {
background?: {
primary?: string; // Main background (default: white/black)
secondary?: string; // Cursor card, cursor bar (default: light/dark gray)
tertiary?: string; // Grid lines, borders (default: lighter gray)
};
text?: {
primary?: string; // Prominent text (default: dark/light)
secondary?: string; // Regular text in cursor card
tertiary?: string; // Labels, axis text
};
};
stages?: Partial<
Record<
SleepStageKey,
{
color?: string; // Override the bar color for a stage
label?: string; // Override the axis label for a stage
}
>
>;
};Theme Customization
Custom Colors
<SleepStagesChart
data={sleepData}
theme={{
colors: {
background: {
primary: "#1a1a2e",
secondary: "#16213e",
tertiary: "#0f3460",
},
text: {
primary: "#e94560",
secondary: "#a8a8a8",
tertiary: "#666666",
},
},
}}
/>Custom Stage Colors and Labels
<SleepStagesChart
data={sleepData}
theme={{
stages: {
awake: { color: "#FF6B6B", label: "Awake" },
rem: { color: "#4ECDC4", label: "REM" },
core: { color: "#45B7D1", label: "Light" },
deep: { color: "#6C5CE7", label: "Deep" },
},
}}
/>Haptic Feedback
Haptic feedback triggers automatically when the user's pan gesture crosses from one sleep segment into another, providing tactile confirmation of segment transitions -- just like the iOS Health app.
- Haptic type:
impactLight(subtle, non-intrusive) - Platform support: iOS (native) and Android (vibration fallback)
- Optional dependency: If
react-native-haptic-feedbackis not installed, haptics are silently disabled - Control: Set
enableHaptics={false}to disable programmatically
Data Format Requirements
- Segments must be sorted by
fromdate in ascending order - Each segment needs a unique
id(used internally for change detection) - Segments should be contiguous (the
toof one segment should match thefromof the next) for proper visual rendering - All segments must fall within the same date -- the chart uses hours/minutes for positioning
- The
typefield must be one of:"awake","rem","core","deep"
Exported Utilities
In addition to the main component, the library exports these utilities:
import {
// Component
SleepStagesChart,
// Default stage definitions (colors, labels, positions)
DEFAULT_STAGES,
// Default color palette
DEFAULT_COLORS,
// Function to merge custom theme with defaults
resolveTheme,
// Types
type SleepSegment,
type SleepStageKey,
type SleepStageDefinition,
type SleepStageMap,
type SleepChartTheme,
type SleepChartThemeColors,
type SleepStagesChartProps,
} from "react-native-apple-sleep-chart";Default Stage Colors
| Stage | Color | Hex |
| ----- | ---------- | --------- |
| Awake | Orange | #FE8A73 |
| REM | Light Blue | #3FB1E7 |
| Core | Blue | #1B81FE |
| Deep | Indigo | #403EA7 |
Wrapping with GestureHandlerRootView
The chart uses react-native-gesture-handler for pan gestures. Your app must be wrapped with GestureHandlerRootView at the root level:
import { GestureHandlerRootView } from "react-native-gesture-handler";
export default function App() {
return (
<GestureHandlerRootView style={{ flex: 1 }}>
{/* Your app content */}
</GestureHandlerRootView>
);
}Running the Example App
# Clone the repo
git clone https://github.com/ddxkalin/react-native-apple-sleep-chart.git
cd react-native-apple-sleep-chart
# Install dependencies
yarn install
# iOS
cd ios && pod install && cd ..
yarn example:ios
# Android
yarn example:androidCompatibility
| Dependency | Minimum Version | | ------------------------------------- | ------------------- | | React | >= 18.0.0 | | React Native | >= 0.70.0 | | react-native-reanimated | >= 3.0.0 | | react-native-gesture-handler | >= 2.0.0 | | react-native-svg | >= 13.0.0 | | @react-native-masked-view/masked-view | >= 0.3.0 | | react-native-haptic-feedback | >= 2.0.0 (optional) |
License
MIT
