| name | expo |
| description | [Applies to: **/*] This guide provides definitive, opinionated best practices for building robust, performant, and maintainable cross-platform mobile applications with Expo, leveraging modern React Native patterns and the latest SDK features. |
| source | cursor_mdc |
expo Best Practices
This document outlines the definitive standards for Expo development. Adhere to these guidelines to ensure consistency, performance, and maintainability across our projects.
1. Code Organization and Structure
Maintain a clean, scalable project structure.
- Root Structure:
assets/: Static assets (images, fonts).
src/: All application logic.
src/components/: Reusable UI components.
src/screens/: Top-level components representing distinct app views.
src/navigation/: Navigation configuration (if not using Expo Router's app/).
src/hooks/: Custom React Hooks.
src/utils/: Utility functions, constants.
src/services/: API clients, data fetching logic.
- Naming Conventions:
- Files/Folders:
kebab-case for directories, PascalCase for components, camelCase for hooks/utils.
- Components:
PascalCase (e.g., Button.tsx, HomeScreen.tsx).
- Variables/Functions:
camelCase (e.g., userName, fetchData).
- Constants:
UPPER_SNAKE_CASE for global constants.
2. Component Best Practices
Prioritize functional components, TypeScript, and clear styling.
- Functional Components with Hooks: Always use functional components.
❌ BAD:
class MyComponent extends React.Component { }
✅ GOOD:
const MyComponent: React.FC = () => { };
- TypeScript for Type Safety: All new code must be in TypeScript.
❌ BAD:
function greet(name) { return `Hello, ${name}`; }
✅ GOOD:
const greet = (name: string): string => `Hello, ${name}`;
- Styling with
StyleSheet: Centralize styles for readability and performance.
❌ BAD:
<Text style={{ fontSize: 16, color: 'blue' }}>Hello</Text>
✅ GOOD:
import { StyleSheet, Text } from 'react-native';
const MyComponent = () => <Text =>Hello;
styles = .({ : { : , : } });
3. Navigation with Expo Router
Leverage file-based routing for intuitive navigation.
4. Data & State Management
Manage state efficiently and immutably.
- Immutable State Updates: Always create new objects/arrays when updating state.
❌ BAD:
const [user, setUser] = useState({ name: 'Alice' });
user.name = 'Bob';
setUser(user);
✅ GOOD:
const [user, setUser] = useState({ name: 'Alice' });
setUser(prev => ({ ...prev, name: 'Bob' }));
useEffect Dependency Arrays: Carefully manage dependencies to prevent unnecessary re-renders or infinite loops.
❌ BAD:
useEffect(() => { fetchData(); });
✅ GOOD:
useEffect(() => { fetchData(); }, []);
useEffect(() => { saveUser(user); }, [user]);
5. Environment Variables
Securely manage environment-specific values.
6. Performance Considerations
Optimize for a smooth user experience.
React.memo for Pure Components: Wrap pure functional components to prevent re-renders when props are unchanged.
❌ BAD:
const MyItem = ({ data }) => { };
✅ GOOD:
const MyItem = React.memo(({ data }) => { });
useCallback and useMemo: Memoize functions and values passed to React.memo components or expensive computations.
❌ BAD:
const handlePress = () => { };
<MyItem onPress={handlePress} />
✅ GOOD:
const handlePress = useCallback(() => { }, []);
<MyItem onPress={handlePress} />
- Lazy Loading Screens: For large apps, lazy load screens with
React.lazy and Suspense.
const LazyScreen = React.lazy(() => import('./LazyScreen'));
7. Error Handling
Implement robust error handling mechanisms.
try/catch for Async Operations: Handle potential errors in asynchronous code.
❌ BAD:
const fetchData = async () => { await api.get('/data'); };
✅ GOOD:
const fetchData = async () => {
try {
await api.get('/data');
} catch (error) {
console.error('Failed to fetch data:', error);
}
};
- Global Error Boundary: Catch UI errors in React components.
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError() { return { hasError: true }; }
render() {
if (this.state.hasError) { return <Text>Something went wrong.</Text>; }
return ..;
}
}
<></>
8. Testing Approaches
Ensure code quality with comprehensive testing.
- Unit Tests with Jest & React Testing Library: Focus on component logic and user interactions. Aim for ≥80% coverage.
❌ BAD: No tests, or only shallow rendering tests.
✅ GOOD:
import { render, fireEvent } from '@testing-library/react-native';
import Button from './Button';
test('renders correctly and calls onPress', () => {
const mockOnPress = jest.fn();
const { getByText } = render(<Button title="Click Me" onPress={mockOnPress} />);
fireEvent.press(getByText('Click Me'));
expect(mockOnPress).toHaveBeenCalledTimes(1);
});
- Snapshot Testing for UI Components: Capture UI structure to detect unintended changes.
import renderer from 'react-test-renderer';
test('renders correctly (snapshot)', () => {
const tree = renderer.create(<Button title="Test" onPress={() => {}} />).();
(tree).();
});