If you are a react native developer beginner, or experience then you must be aware that code practices is a non-negotiable skill. As a developer, delivery of a project is a must but writing a scalable, and quality code will help you, and your team in future.
Before we move ahead, these practices can be work on React Native CLI, or Expo project. From 2024, as per RN team, Expo would be the official framework to build the react native projects.
In this blog, we will learn about code practices for react native projects. Remember a good project is a balance of:
scalable
consistency
maintainable
Readability
Read my blog on how to start with React Native developer as ReactJS developer
1. Project structure
The first thing of a developer is to have a maintainable , readable, and scalable codebase. Your project structure will help the future developers too. With Expo, one has the structure of the project but as a react native developer and based on your project you should plan your project's structure:
my-app/
├── assets/
│ ├── fonts/
│ ├── images/
│ └── icons/
├── components/
│ ├── Button.js
│ ├── Button.styles.js
│ └── Header.js
├── screens/
│ ├── HomeScreen/
│ │ ├── HomeScreen.js
│ │ └── HomeScreen.styles.js
│ └── ProfileScreen/
│ ├── ProfileScreen.js
│ └── ProfileScreen.styles.js
├── navigation/
│ ├── AppNavigator.js
│ ├── AuthNavigator.js
│ └── MainNavigator.js
├── redux/ (or store/ if using Zustand, MobX, etc.)
│ ├── actions/
│ ├── reducers/
│ ├── store.js
│ └── types.js
├── services/
│ ├── api.js
│ └── auth.js
├── utils/
│ ├── helpers.js
│ └── constants.js
├── App.js
├── package.json
├── .babelrc
└── README.md
Update: please remember as per your requirement do explore domain or feature specific architecture. Thank you B Camphart, and Idris Gadi
2. imports alias
Long import paths can make your code harder to read and maintain. Instead of writing long relative paths like ../../../components/Button
, use aliases to shorten them and make your code more readable.
import Button from 'components/ui/Button';
import Header from 'components/layout/Header';
3. imports order
To automatically manage the order of your imports, you can configure Babel with a plugin that handles this for you. This keeps your imports clean and reduces manual intervention.
npm install --save-dev babel-plugin-module-resolver
4. TypeScript
There’s little debate when it comes to choosing between TypeScript (TS) and JavaScript (JS), especially for large-scale applications. TypeScript provides static type checking, which helps catch errors at compile-time rather than runtime, leading to more reliable and maintainable code.
5. Style
There are multiple ways one can style their RN projects. One can use, NativeWind, or styled from React native. With so many options, one should go for the consistency, scalable, and maintainable. Read my blog on styling here
1. inline: Not a good approach for large scale projects at all.
<View style={{ backgroundColor: 'blue', padding: 10 }}>
<Text style={{ color: 'white' }}>Hello</Text>
</View>
2. StyleSheet API: It is good but styles won't be reusable
import { StyleSheet, View, Text } from 'react-native';
const styles = StyleSheet.create({
container: {
backgroundColor: 'blue',
padding: 10,
},
text: {
color: 'white',
},
});
const App = () => (
<View style={styles.container}>
<Text style={styles.text}>Hello</Text>
</View>
);
3. Separate style: It is my prefer way of styling for large projects. Create a separate style.js
and use that in the components you require.
/components
├── MyComponent.js
├── MyComponent.styles.js
/App.js
// MyComponent.styles.js
import { StyleSheet } from 'react-native';
export default StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#f5f5f5',
},
title: {
fontSize: 24,
fontWeight: 'bold',
color: '#333',
marginBottom: 20,
},
button: {
backgroundColor: '#007bff',
paddingVertical: 10,
paddingHorizontal: 20,
borderRadius: 5,
},
buttonText: {
color: '#fff',
fontSize: 16,
fontWeight: '600',
},
});
// MyComponent.js
import React from 'react';
import { View, Text, Pressable } from 'react-native';
import styles from './MyComponent.styles';
const MyComponent = () => {
return (
<View style={styles.container}>
<Text style={styles.title}>Hello from MyComponent</Text>
<Pressable style={styles.button}>
<Text style={styles.buttonText}>Click Me</Text>
</Pressable>
</View>
);
};
export default MyComponent;
4. styled components: Another way for large projects.
UPDATE: Please check this
Apparently, Styled Components has performance issues: https://github.com/styled-components/styled-components/issues/3940
import styled from 'styled-components/native';
const Container = styled.View`
background-color: blue;
padding: 10px;
`;
const StyledText = styled.Text`
color: white;
`;
const App = () => (
<Container>
<StyledText>Hello</StyledText>
</Container>
);
5. native wind: NativeWind is a good way to style your app. After installing the native wind you can use the classes to style your app. By this you are delegating the styling work.
import React from 'react';
import { View, Text, Pressable } from 'react-native';
import { styled } from 'nativewind';
const App = () => {
return (
<View className="flex-1 justify-center items-center bg-gray-100">
<Text className="text-2xl font-bold text-blue-500 mb-4">
Welcome to NativeWind!
</Text>
<Pressable className="bg-blue-500 px-4 py-2 rounded">
<Text className="text-white font-semibold">Press Me</Text>
</Pressable>
</View>
);
};
export default App;
6. props
Props are used to communicate between components in React Native, allowing data to flow from parent components to child components. Just like styling, there are multiple ways to manage props. Consistency is key, so it's recommended to stick to one approach throughout your project.
Additionally, always destructure props for cleaner and more readable code. Destructuring not only improves readability but also makes it easier to spot which props a component is using.
const MyComponent = ({ title, subtitle }) => {
return (
<View>
<Text>{title}</Text>
<Text>{subtitle}</Text>
</View>
);
};
7. State management
Efficient state management ensures that the app remains performant and manageable as the codebase grows. In today's time we have a lot of options to pick the best state management.
a. Prefer local state over global state
b. Use Context API
for simple state
c. Use a State Management Library for Complex State
d. Immutable State Updates
e. Prefer redux toolkit
over redux
import { createSlice } from '@reduxjs/toolkit';
const booksSlice = createSlice({
name: 'books',
initialState: [],
reducers: {
addBook: (state, action) => {
state.push(action.payload);
},
removeBook: (state, action) => {
return state.filter(book => book.id !== action.payload);
},
},
});
export const { addBook, removeBook } = booksSlice.actions;
export default booksSlice.reducer;
8. Crash Analytics
To ensure your app's health and reduce crashes, it's important to implement crash analytics and error tracking:
a. Use Crash Analytics Tools: Implement services like - Firebase Crashlytics
, or Sentry
b. Test your App's stability
Run automated tests and manual stress testing to catch edge-case crashes. Utilize services like TestFlight or Google Play Beta Testing.
You can track both native crashes (iOS/Android) and JavaScript errors. Use ErrorBoundary to catch JavaScript errors and log them to a crash analytics service.
c. Track JS and Native Errors
import React from 'react';
import * as Sentry from '@sentry/react-native';
class ErrorBoundary extends React.Component {
componentDidCatch(error, errorInfo) {
Sentry.captureException(error, { extra: errorInfo });
}
render() {
if (this.state.hasError) {
return <Text>Something went wrong.</Text>;
}
return this.props.children;
}
}
9. Logging
Logging helps track app behaviour, debug issues, and gather analytics.
a. Use a Logging Framework
React Native Logger: An easy-to-use logger specifically designed for React Native.
Winston: A multi-transport logging library that can work with both React Native and Node.js.
import logger from 'react-native-logger';
logger.log('This is a debug log');
logger.warn('This is a warning log');
logger.error('This is an error log');
b. Differentiate Log Levels
Use appropriate log levels like
debug
,info
,warn
, anderror
.In production, minimize logging verbosity by only allowing error and warn logs, while in development mode, use debug and info.
c. Remote Logging
Consider sending logs to a remote logging service such as:
Papertrail
Loggly
Firebase Analytics
d. Log Sensitive Information Carefully
Avoid logging sensitive user information like passwords, tokens, or personal data.
10. Testing
Testing for every project is crucial. As a developer, quality is the responsibility of the developer. In React native world there are:
Unit testing
Integration testing
End to End testing
Do spend time atleast on the end to end testing. There are many tools available for the testing.
Happy Learning!! Say hi to me at Twitter
Top comments (10)
I see this sort of architecture all time. At scale, it can become a nightmare to jump around in. Of course you'll always have top-level shared code for ui components and things, but my preference is to organize the code around the domain and the use cases within them.
What I have found is that any project that gets big enough will suffer navigation issues, there are many reasons but one major reason is that even if the project had initially started with some project structure in mind, as more and new people start committing to the project they start doing what they are comfortable with and it gets messy real quick.
One way to solve this issue is to have some documentation around the project structure, I like bulletproof react project structure. You don't need to follow their exact structure but can use some variation of it and copy their documentation style.
Idris, and B Camphart , I have updated the blog with your inputs. Thank you. IMO, all 3 approaches are fine as per the developer's requirements. Hence, added the same :) Thanks
Thank you for sharing. Let me add this as an another option in the blog.
React Native has been on my learning list for a while, and this feels like the perfect sign to get started. I'll take a look at your other posts and follow up with you, if you don't mind.
Woah!! nice to e-meet someone who believes on signs (I do a lot).
please start with React Native and feel free to ask/reach out to me.
All the best
I use atomic design structure to create my components.
Nice article, though.
I will check NativeWind.
Thank you @jeannitonmnr.
Thanks for mentioning my explanation on styled components and linking the benchmark here.
thanks to you for sharing your learning.