Breathing Exercise
The @breathing-exercise module provides a comprehensive solution for implementing guided breathing exercises in React Native applications. It combines smooth animations, real-time progress tracking, and flexible configuration to create an engaging and therapeutic breathing experience.
Overview
The Breathing Exercise module offers a complete compound component system for creating guided breathing sessions. It features synchronized Lottie animations, real-time progress tracking, customizable breathing patterns, and a modular architecture that allows for flexible UI composition.
Key Features
- 🎯 Guided Breathing Patterns: Configurable inhale-hold-exhale sequences
- 🎨 Smooth Animations: Lottie-based animations synchronized with breathing phases
- 📊 Real-time Progress: Live progress tracking and remaining time display
- 🔄 Customizable Cycles: Configurable repetition counts and timing
- 🧩 Compound Components: Flexible UI composition with sub-components
- ♿ Accessibility: Full screen reader and keyboard navigation support
- 🌐 Internationalization: Multi-language support for method names
- ⚡ Performance: Optimized with React Native Reanimated
- 🎭 Theming: Integration with React Native Paper theming
Key Components
Primary Components
BreathingExercise: Main compound component containerBreathingExerciseProvider: Context provider for state managementuseBreathingExercise: Hook for accessing exercise state
Sub-Components
ProgressBar: Visual progress indicatorAnimationWrapper: Container for breathing animationAnimation: Lottie animation componentMethodTypeIcon: Icon for current breathing phaseInfo: Information display containerMethodName: Current phase name displayRemainingTime: Time countdown display
Icon Components
InhaleIcon: Inhale phase iconHoldIcon: Hold phase iconExhaleIcon: Exhale phase icon
Quick Start
Basic Usage
import { BreathingExercise } from "@breathing-exercise";
export const BasicBreathingSession = () => {
const handleFinish = () => {
console.log("Breathing exercise completed!");
};
return (
<BreathingExercise
methods={[
{ type: "inhale", duration: 4 },
{ type: "hold", duration: 4 },
{ type: "exhale", duration: 4 },
]}
repeat={5}
onFinish={handleFinish}
>
<BreathingExercise.ProgressBar />
<BreathingExercise.AnimationWrapper>
<BreathingExercise.Animation />
</BreathingExercise.AnimationWrapper>
<BreathingExercise.Info>
<BreathingExercise.MethodTypeIcon />
<BreathingExercise.MethodName variant="headlineMedium" />
<BreathingExercise.RemainingTime />
</BreathingExercise.Info>
</BreathingExercise>
);
};
Advanced Configuration
import { BreathingExercise } from "@breathing-exercise";
import { View, StyleSheet } from "react-native";
export const Advanced478BreathingSession = () => {
const handleFinish = () => {
// Navigate to completion screen or show success message
};
return (
<View style={styles.container}>
<BreathingExercise
methods={[
{ type: "inhale", duration: 4 },
{ type: "hold", duration: 7 },
{ type: "exhale", duration: 8 },
]}
repeat={10}
onFinish={handleFinish}
style={styles.exercise}
>
<BreathingExercise.ProgressBar style={styles.progressBar} />
<BreathingExercise.AnimationWrapper style={styles.animationContainer}>
<BreathingExercise.Animation
style={styles.animation}
resizeMode="contain"
/>
</BreathingExercise.AnimationWrapper>
<BreathingExercise.Info style={styles.info}>
<View style={styles.iconContainer}>
<BreathingExercise.MethodTypeIcon size={48} />
</View>
<BreathingExercise.MethodName
variant="headlineLarge"
style={styles.methodName}
/>
<BreathingExercise.RemainingTime
variant="bodyLarge"
style={styles.remainingTime}
/>
</BreathingExercise.Info>
</BreathingExercise>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
},
exercise: {
flex: 1,
},
progressBar: {
marginBottom: 40,
},
animationContainer: {
flex: 1,
justifyContent: "center",
},
animation: {
width: "100%",
height: "100%",
},
info: {
alignItems: "center",
gap: 16,
},
iconContainer: {
marginBottom: 8,
},
methodName: {
textAlign: "center",
},
remainingTime: {
opacity: 0.7,
},
});
Breathing Patterns
Common Patterns
4-4-4 (Box Breathing)
const boxBreathing = [
{ type: "inhale", duration: 4 },
{ type: "hold", duration: 4 },
{ type: "exhale", duration: 4 },
];
4-7-8 (Relaxing Breath)
const relaxingBreath = [
{ type: "inhale", duration: 4 },
{ type: "hold", duration: 7 },
{ type: "exhale", duration: 8 },
];
4-2-4-2 (Coherent Breathing)
const coherentBreathing = [
{ type: "inhale", duration: 4 },
{ type: "hold", duration: 2 },
{ type: "exhale", duration: 4 },
];
Architecture
Component Hierarchy
BreathingExercise (Provider + Container)
├── BreathingExercise.ProgressBar
├── BreathingExercise.AnimationWrapper
│ └── BreathingExercise.Animation
├── BreathingExercise.Info
│ ├── BreathingExercise.MethodTypeIcon
│ ├── BreathingExercise.MethodName
│ └── BreathingExercise.RemainingTime
State Management
The module uses React Context with React Native Reanimated for state management:
- Context Provider:
BreathingExerciseProvidermanages the exercise state - Shared Values: Progress and timing calculations using
useSharedValue - Derived Values: Real-time calculations for animation and display
- State Synchronization: UI updates synchronized with animation progress
Animation System
- Lottie Integration: Uses
react-native-lottiefor smooth breathing animations - Progress Synchronization: Animation progress tied to breathing phase progress
- Performance Optimized: Runs on UI thread using React Native Reanimated
Styling
Theme Integration
The module integrates with React Native Paper's theming system:
import { useAppTheme } from "@hooks/use-app-theme";
const MyBreathingSession = () => {
const theme = useAppTheme();
return (
<BreathingExercise
// ... exercise props
style={{ backgroundColor: theme.colors.surface }}
>
<BreathingExercise.MethodName style={{ color: theme.colors.primary }} />
{/* ... other components */}
</BreathingExercise>
);
};
Custom Styling
All components accept standard React Native style props:
<BreathingExercise style={customContainerStyle}>
<BreathingExercise.Animation style={customAnimationStyle} />
<BreathingExercise.MethodName style={customTextStyle} />
</BreathingExercise>
Accessibility
Screen Reader Support
- Semantic Labels: All components have appropriate accessibility labels
- Live Regions: Progress and time updates announced to screen readers
- Content Description: Current breathing phase announced
Implementation
<BreathingExercise
accessible={true}
accessibilityRole="timer"
accessibilityLabel="Breathing exercise in progress"
>
<BreathingExercise.MethodName
accessibilityLiveRegion="polite"
accessibilityRole="text"
/>
<BreathingExercise.RemainingTime
accessibilityLiveRegion="polite"
accessibilityRole="timer"
/>
</BreathingExercise>
Performance
Optimization Features
- UI Thread Animation: Critical animations run on UI thread
- Memoized Calculations: Expensive calculations are memoized
- Selective Re-renders: Only necessary components re-render
- Efficient State Updates: Minimal state updates using shared values
Best Practices
// ✅ Good: Memoize breathing patterns
const breathingPattern = React.useMemo(
() => [
{ type: "inhale", duration: 4 },
{ type: "hold", duration: 4 },
{ type: "exhale", duration: 4 },
],
[],
);
// ✅ Good: Memoize callback functions
const handleFinish = React.useCallback(() => {
// Handle completion
}, []);
Testing
Component Testing
import { render, screen } from "@testing-library/react-native";
import { BreathingExercise } from "@breathing-exercise";
describe("BreathingExercise", () => {
const mockProps = {
methods: [
{ type: "inhale", duration: 4 },
{ type: "hold", duration: 4 },
{ type: "exhale", duration: 4 },
],
repeat: 1,
onFinish: jest.fn(),
};
it("renders breathing exercise correctly", () => {
render(
<BreathingExercise {...mockProps}>
<BreathingExercise.MethodName />
<BreathingExercise.RemainingTime />
</BreathingExercise>,
);
expect(screen.getByRole("timer")).toBeOnTheScreen();
});
it("displays current method name", () => {
render(
<BreathingExercise {...mockProps}>
<BreathingExercise.MethodName />
</BreathingExercise>,
);
expect(screen.getByText(/inhale/i)).toBeOnTheScreen();
});
});
Animation Testing
import { renderHook } from "@testing-library/react-hooks";
import { useBreathingExercise } from "@breathing-exercise";
describe("useBreathingExercise", () => {
it("provides exercise context", () => {
const wrapper = ({ children }) => (
<BreathingExercise {...mockProps}>{children}</BreathingExercise>
);
const { result } = renderHook(() => useBreathingExercise(), { wrapper });
expect(result.current.progress).toBeDefined();
expect(result.current.methodType).toBe("inhale");
});
});
Common Patterns
Session Management
const useBreathingSession = () => {
const [isActive, setIsActive] = React.useState(false);
const [completedSessions, setCompletedSessions] = React.useState(0);
const startSession = () => setIsActive(true);
const pauseSession = () => setIsActive(false);
const handleFinish = React.useCallback(() => {
setIsActive(false);
setCompletedSessions((prev) => prev + 1);
}, []);
return {
isActive,
completedSessions,
startSession,
pauseSession,
handleFinish,
};
};
Custom Progress Tracking
const BreathingWithAnalytics = () => {
const [analytics, setAnalytics] = React.useState({
startTime: null,
phaseChanges: 0,
});
const handlePhaseChange = React.useCallback((phase) => {
setAnalytics((prev) => ({
...prev,
phaseChanges: prev.phaseChanges + 1,
}));
}, []);
return (
<BreathingExercise
onFinish={() => {
// Send analytics data
console.log("Session analytics:", analytics);
}}
>
{/* Components */}
</BreathingExercise>
);
};
Best Practices
1. Exercise Configuration
// ✅ Good: Define patterns as constants
const BREATHING_PATTERNS = {
BOX: [
{ type: "inhale", duration: 4 },
{ type: "hold", duration: 4 },
{ type: "exhale", duration: 4 },
],
RELAXING: [
{ type: "inhale", duration: 4 },
{ type: "hold", duration: 7 },
{ type: "exhale", duration: 8 },
],
} as const;
2. Error Handling
const SafeBreathingExercise = (props) => {
try {
return <BreathingExercise {...props} />;
} catch (error) {
console.error("Breathing exercise error:", error);
return <FallbackComponent />;
}
};
3. Responsive Design
const ResponsiveBreathingExercise = () => {
const { width, height } = useWindowDimensions();
const isLandscape = width > height;
return (
<BreathingExercise style={isLandscape ? styles.landscape : styles.portrait}>
{/* Adaptive layout */}
</BreathingExercise>
);
};
Dependencies
Core Dependencies
react: ^18.0.0react-native: ^0.72.0react-native-reanimated: ^3.0.0react-native-paper: ^5.0.0react-i18next: ^13.0.0lottie-react-native: ^6.0.0
Peer Dependencies
react-native-svg: ^13.0.0 (for icons)react-native-vector-icons: ^10.0.0 (optional)
TypeScript Support
The module is fully typed with comprehensive TypeScript definitions:
import type {
BreathingExercise,
BreathingExerciseProps,
BreathingExerciseContextType,
BreathingExerciseAnimationProps,
} from "@breathing-exercise";
Migration Guide
From Custom Implementation
If migrating from a custom breathing exercise implementation:
- Replace Custom Timer Logic: Use the provided
BreathingExerciseProvider - Update Animation Integration: Migrate to the Lottie-based
Animationcomponent - Adopt Compound Components: Break down UI into the provided sub-components
- Update State Management: Use the
useBreathingExercisehook
Breaking Changes
- v2.0.0: Removed deprecated
durationprop in favor ofmethodsarray - v1.5.0: Changed
onCompletetoonFinishfor consistency
For detailed component-specific documentation, see the individual component pages in the sidebar.