DEV Community

Cover image for React Native Best Practices
Neha Sharma
Neha Sharma

Posted on • Updated on

React Native Best Practices

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:

  1. scalable

  2. consistency

  3. maintainable

  4. 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

Enter fullscreen mode Exit fullscreen mode

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';
Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
);
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
// 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',
  },
});
Enter fullscreen mode Exit fullscreen mode
// 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;

Enter fullscreen mode Exit fullscreen mode

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>
);

Enter fullscreen mode Exit fullscreen mode

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;

Enter fullscreen mode Exit fullscreen mode

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>
  );
};

Enter fullscreen mode Exit fullscreen mode

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;

Enter fullscreen mode Exit fullscreen mode

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;
  }
}
Enter fullscreen mode Exit fullscreen mode

9. Logging

Logging helps track app behaviour, debug issues, and gather analytics.

a. Use a Logging Framework

  1. React Native Logger: An easy-to-use logger specifically designed for React Native.

  2. 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');
Enter fullscreen mode Exit fullscreen mode

b. Differentiate Log Levels

  1. Use appropriate log levels like debug, info, warn, and error.

  2. 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:

  1. Papertrail

  2. Loggly

  3. 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:

  1. Unit testing

  2. Integration testing

  3. End to End testing

Do spend time atleast on the end to end testing. There are many tools available for the testing.

Happy Learning!!

Top comments (8)

Collapse
 
bcamphart profile image
B. Camphart

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.

Collapse
 
igadii profile image
Idris Gadi

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.

Collapse
 
hellonehha profile image
Neha Sharma

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

Collapse
 
hellonehha profile image
Neha Sharma

Thank you for sharing. Let me add this as an another option in the blog.

Collapse
 
avdev profile image
Layade Victor

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.

Collapse
 
hellonehha profile image
Neha Sharma

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

Collapse
 
jeanniton-mnr profile image
Monero Jeanniton

I use atomic design structure to create my components.

Nice article, though.

I will check NativeWind.

Collapse
 
hellonehha profile image
Neha Sharma

Thank you @jeannitonmnr.