Navigate back to the homepage

React Native Project Structure

Infinitbility
Project Structure
January 9th, 2021 · 4 min read
React Native Project Structure

This Article Part of React Native Tutorial Series want to start from scrach follow below link

https://infinitbility.com/react-native/table-of-contents

Today, we setup the react native app with an example of project folder structure, React Navigation, and Axios also you guys use for the starter.

Let’s discuss a structure to start new projects or when you need to scale large projects.

We will use React Native as a basis for this architecture, but the concepts can be leveraged in projects using other frameworks.

For the purpose of this post, I will use the following patterns and packages:

– React Navigation: Routing and navigation for your React Native apps;

– Axios: Promise-based HTTP client for the browser and node.js.

Follow the required installation steps on Getting Started · React Native. After configuring the React Native CLI on your machine, verify if react-native -v is available on your terminal.

You should get a return similar to this:

1$ react-native-cli: 2.0.1

So we can proceed to create our project.


Creating a project from Scratch

Choose a directory of your choice to create our base project using the following command:

1$ react-native init ReactNativeExample

After the execution, you can access the directory using the cd ReactNativeExample. You will get a structure similar to this:

1.
2├── __tests__
3│ ├── App-test.js
4├── android
5├── ios
6├── node_modules
7├── .buckconfig
8├── .eslintrc.js
9├── .flowconfig
10├── .gitattributes
11├── .gitignore
12├── .prettierrc.js
13├── .watchmanconfig
14├── App.js
15├── app.json
16├── babel.config.js
17├── index.js
18├── metro.config.js
19├── package.json
20└── yarn.lock

Structuring the pillars

We will define a structure for our project to grow efficiently and make its maintenance easier.

Our first step will be to define the directory structure within src (Source). This directory will contain all major project files.

Let’s create the following initial structure for our project:

1├── src
2│ ├── assets
3│ │ ├── fonts
4│ │ ├── images
5│ ├── components
6│ │ ├── atoms
7│ │ ├── molecules
8│ │ ├── organisms
9│ ├── navigations
10│ ├── scenes
11│ ├── styles
12│ ├── utils
13│ ├── index.js

Right now you might be wondering why there are so many folders and files, but don’t worry, further on the post we’ll go over their purpose and how important each one of them is.

Enabling the use of the alias

To simplify the require/import of paths in our project, we must configure directory aliases. So let’s install the following packages:

1$ yarn add -D [email protected]^5.1.0

After installing the dependencies, let’s configure the .babelrc:

.babelrc

1{
2 "plugins": [
3 [
4 "module-resolver",
5 {
6 "cwd": "babelrc",
7 "root": ["./src"],
8 "extensions": [".js", ".ios.js", ".android.js"],
9 "alias": {
10 "_assets": "./src/assets",
11 "_components": "./src/components",
12 "_atoms": "./src/components/atoms",
13 "_molecules": "./src/components/molecules",
14 "_organisms": "./src/components/organisms",
15 "_navigations": "./src/navigations",
16 "_scenes": "./src/scenes",
17 "_services": "./src/services",
18 "_styles": "./src/styles",
19 "_utils": "./src/utils"
20 }
21 }
22 ]
23 ]
24}

Edit the .eslintrc.js file to avoid lint errors when using the new alias:

1module.exports = {
2 root: true,
3 extends: '@react-native-community',
4 plugins: ['import'],
5 settings: {
6 'import/resolver': {
7 node: {
8 paths: ['src'],
9 alias: {
10 _assets: './src/assets',
11 _components: './src/components',
12 _atoms: './src/components/atoms',
13 _molecules: './src/components/molecules',
14 _organisms: './src/components/organisms',
15 _navigations: './src/navigations',
16 _scenes: './src/scenes',
17 _services: './src/services',
18 _styles: './src/styles',
19 _utils: './src/utils',
20 },
21 },
22 },
23 },
24};

Read more about alias setup at Babel Plugin Module Resolver.


Enable editors to alias autocompletion

Create the jsconfig.json file and use the same alias that was defined in .babelrc. Check it out below:

1{
2 "compilerOptions": {
3 "baseUrl": ".",
4 "paths": {
5 "_assets": ["src/assets/*"],
6 "_components": ["src/components/*"],
7 "_atoms": ["src/components/atoms/*"],
8 "_molecules": ["src/components/molecules/*"],
9 "_organisms": ["src/components/organisms/*"],
10 "_navigations": ["src/navigations/*"],
11 "_scenes": ["src/scenes/*"],
12 "_services": ["src/services/*"],
13 "_styles": ["src/styles/*"],
14 "_utils": ["src/utils/*"]
15 }
16 }
17}

Once you have edited it, it’s time to test the alias. Let’s edit our src/index.js file by adding a test component as follows:

1import React from 'react';
2import {View,Text} from 'react-native';
3
4const App = () => (
5 <View>
6 <Text>Hello World</Text>
7 </View>
8);
9
10export default App;

Now in our index.js in the project root we will import the App component as follows:

1import App from './src';

This way you will have your alias set up working on your project.


Atomic Components

We will not dive into the concepts of atomic, only the organization we have chosen to use in this project.

– Atoms – The smallest possible components, such as buttons, titles, inputs or event color pallets, animations, and fonts.

– Molecules – They are the composition of one or more components of atoms.

– Organisms – The combination of molecules that work together or even with atoms that compose more elaborate interfaces.

Remembering the directories we use to organize your components:

1├── src
2│ ├── components
3│ │ ├── atoms
4│ │ ├── molecules
5│ │ ├── organisms

In each component directory, we have an index.js file that exports the specified category.

Let’s create a component called HelloWorld in src/atoms to understand the idea:

src/atoms/hello-world.js

1import React from 'react';
2import {Text} from 'react-native';
3
4const HelloWorld = ({name}) => <Text>Hello World {name}!</Text>;
5
6export default HelloWorld;

We export as follows:

src/atoms/index.js

1export {default as HelloWorld} from './hello-world';

Now we can use this component in src/index.js:

1import React from 'react';
2import {HelloWorld} from '_atoms';
3
4const App = () => <HelloWorld name="Helder Burato Berto" />;
1export default App;

Note: The App.js in the project root can be removed, it will no longer be used.


Our Scenes

I have a habit of dividing every application screen as a scene and so each one has its directory.

We will define some scenes that will not be used this time, and then we will configure these navigations using the created screens.

1.
2├── src
3│ ├── scenes
4│ │ ├── login
5│ │ │ ├── index.js // LoginScreen
6│ │ ├── home
7│ │ │ ├── index.js // HomeScreen
8│ │ ├── about
9│ │ │ ├── index.js // AboutScreen

At the Login screen, we will add a navigation button to go to the Home screen. See below:

1import React from 'react';
2import {SafeAreaView, Text, TouchableHighlight} from 'react-native';
3
4const LoginScreen = ({navigation}) => (
5 <SafeAreaView>
6 <Text>Screen: Login</Text>
7
8 <TouchableHighlight onPress={() => navigation.navigate('Home')}>
9 <Text>Go to home</Text>
10 </TouchableHighlight>
11 </SafeAreaView>
12);
13
14export default LoginScreen;

Note: The navigation object will be available on all screens that are surrounded by the navigator object.


Ways of Navigation

In this step, we will need to add some new dependencies to the project. See below:

You can read more about it https://reactnavigation.org/docs/en/getting-started.html.

In our application, we will have two types of navigation.

In the Login screen, we will have Stack navigation type and in the rest of the app we will have Tab navigation type.

Note: This is just an example of navigation, not a pattern. If you need to use other types of navigation, you can create them at the src/navigations.

In the src/navigations directory we will define the following structure:

1├── src
2│ ├── navigations
3│ │ ├── index.js // RootNavigator
4│ │ ├── auth-navigator.js // AuthNavigator
5│ │ ├── app-navigator.js // AppNavigator
  1. In the auth-navigator.js we will define the navigation type for the login screen:

    1import {createStackNavigator} from 'react-navigation-stack';
    2
    3import LoginScreen from '_scenes/login';
    4
    5const AuthNavigatorConfig = {
    6initialRouteName: 'Login',
    7header: null,
    8headerMode: 'none',
    9};
    10
    11const RouteConfigs = {
    12Login:LoginScreen,
    13};
    14
    15const AuthNavigator = createStackNavigator(RouteConfigs, AuthNavigatorConfig);
    16
    17export default AuthNavigator;
  2. In the app-navigator.js we will define the type of navigation to internal screens app:

    1import {createBottomTabNavigator} from 'react-navigation-tabs';
    2
    3import HomeScreen from '_scenes/home';
    4import AboutScreen from '_scenes/about';
    5
    6const TabNavigatorConfig = {
    7initialRouteName: 'Home',
    8header: null,
    9headerMode: 'none',
    10};
    11
    12const RouteConfigs = {
    13Home:{
    14 screen:HomeScreen,
    15},
    16About:{
    17 screen:AboutScreen,
    18},
    19};
    20
    21const AppNavigator = createBottomTabNavigator(RouteConfigs, TabNavigatorConfig);
    22
    23export default AppNavigator;
  3. In the index.js we will define our RootNavigator merging the auth and app navigators:

    1import {createAppContainer, createSwitchNavigator} from 'react-navigation';
    2
    3import AuthNavigator from './auth-navigator';
    4import AppNavigator from './app-navigator';
    5
    6const RootNavigator = createSwitchNavigator(
    7{
    8 Auth: AuthNavigator,
    9 App: AppNavigator,
    10},
    11{
    12 initialRouteName: 'Auth',
    13},
    14);
    15
    16export default createAppContainer(RootNavigator);

    Now you can import the Navigator object into your src/index.js as follows:

    1import React from 'react';
    2
    3import Navigator from '_navigations';
    4
    5const App = () => <Navigator />;
    6
    7export default App;

    This way you will have simple and functional navigation.


Reusable Services

Not everything can be considered a component in React Native, a well-known approach used to create separate modules and in some cases containing business rules are the use of services.

Directory for creating services:

src/services

They can be shared with multiple screens and components in your project.

Commonly used to create services that make contact with external APIs and use the axios library that we mentioned at the beginning of the post.


Shared Styles

Based in CSS preprocessor we use some default files in our style structure:

1.
2├── src
3│ ├── styles
4│ │ ├── index.js // Export all
5│ │ ├── colors.js // Colors pallet
6│ │ ├── mixins.js // Mixins to use CSSinJS
7│ │ ├── spacing.js // Paddings, margins and scale
8│ │ ├── typography.js // Fonts types and sizes

index.js

1import * as Colors from './colors';
2import * as Spacing from './spacing';
3import * as Typography from './typography';
4import * as Mixins from './mixins';
5
6export { Typography, Spacing, Colors, Mixins };

colors.js

1export const PRIMARY = '#1779ba';
2export const SECONDARY = '#767676';
3export const WHITE = '#FFFFFF';
4export const BLACK = '#000000';
5
6// ACTIONS
7export const SUCCESS = '#3adb76';
8export const WARNING = '#ffae00';
9export const ALERT = '#cc4b37';
10
11// GRAYSCALE
12export const GRAY_LIGHT = '#e6e6e6';
13export const GRAY_MEDIUM = '#cacaca';
14export const GRAY_DARK = '#8a8a8a';

mixins.js

1import {Dimensions,PixelRatio} from 'react-native';
2
3const WINDOW_WIDTH = Dimensions.get('window').width;
4const guidelineBaseWidth = 375;
5
6export const scaleSize = size => (WINDOW_WIDTH/guidelineBaseWidth) * size;
7
8export const scaleFont = size => size * PixelRatio.getFontScale();
9
10function dimensions(top, right = top, bottom = top, left = right, property){
11 let styles = {};
12
13 styles[`${property}Top`] = top;
14 styles[`${property}Right`] = right;
15 styles[`${property}Bottom`] = bottom;
16 styles[`${property}Left`] = left;
17
18 return styles;
19}
20
21export function margin(top, right, bottom, left){
22 return dimensions(top, right, bottom, left, 'margin');
23}
24
25export function padding(top, right, bottom, left){
26 return dimensions(top, right, bottom, left, 'padding');
27}
28
29export function boxShadow(color, offset = {height:2,width:2},
30 radius = 8, opacity = 0.2){
31 return {
32 shadowColor: color,
33 shadowOffset: offset,
34 shadowOpacity: opacity,
35 shadowRadius: radius,
36 elevation: radius,
37 };
38}

spacing.js

1import {scaleSize} from './mixins';
2
3export const SCALE_18 = scaleSize(18);
4export const SCALE_16 = scaleSize(16);
5export const SCALE_12 = scaleSize(12);
6export const SCALE_8 = scaleSize(8);

typography.js

1import { scaleFont } from './mixins';
2
3// FONT FAMILY
4export const FONT_FAMILY_REGULAR = 'OpenSans-Regular';
5export const FONT_FAMILY_BOLD = 'OpenSans-Bold';
6
7// FONT WEIGHT
8export const FONT_WEIGHT_REGULAR = '400';
9export const FONT_WEIGHT_BOLD = '700';
10
11// FONT SIZE
12export const FONT_SIZE_16 = scaleFont(16);
13export const FONT_SIZE_14 = scaleFont(14);
14export const FONT_SIZE_12 = scaleFont(12);
15
16// LINE HEIGHT
17export const LINE_HEIGHT_24 = scaleFont(24);
18export const LINE_HEIGHT_20 = scaleFont(20);
19export const LINE_HEIGHT_16 = scaleFont(16);
20
21// FONT STYLE
22export const FONT_REGULAR = {
23 fontFamily: FONT_FAMILY_REGULAR,
24 fontWeight: FONT_WEIGHT_REGULAR,
25};
26
27export const FONT_BOLD = {
28 fontFamily: FONT_FAMILY_BOLD,
29 fontWeight: FONT_WEIGHT_BOLD,
30};

This is our basic style settings to structure our application.

This way you can import into any of your components the following Typography, Spacing, Colors, Mixins objects, which will have access to the functionality of each style object.

Extra: Custom Font To enable custom fonts you need to create the react-native.config.js in the project root and set the directory where your .ttf files are as follows:

1module.exports = {
2 assets:['./src/assets/fonts/'],
3};

After that, you should run the react-native link to link your fonts to the iOS / Android native code.


Defining Utils

We have the src/utils directory where we add all our utility/helper methods that can be shared across our entire project.

Whenever you come across situations where you get caught repeating code is a good situation to create a util/helper.


Wrapping up

I have been working with this format on the last React Native projects I worked on and I can say that it helped me a lot regarding the organization and development of the project.

This is just one way we found to be productive and better organized among our team, I hope it helps you too.

Feel free to comment below if you have any questions, I’ll be happy to help you.

Enjoy programming!

The repository of this example is available at ReactNativeExample.

More From React Native Tutorial

Basics

  1. Introduction To React Native
  2. React Native Environment Setup using expo
  3. React Native Environment Setup for windows
  4. React Native Environment setup on Mac OS
  5. React Native Environment setup on linux
  6. React Native Project Structure
  7. React Native State
  8. React Native Props
  9. React Native Styling
  10. React Native Flexbox
  11. React Native Text
  12. React Native Textinput
  13. React Native Commands
  14. React Native ScrollView

Advances

  1. React Native Dark Mode
  2. React Native Fonts
  3. React Native SQLite
  4. React Native DatepickerAndroid
  5. React native ScrollView scroll to position
  6. How to align icon with text in react native
  7. React Native Image
  8. React Native Firebase Crashlytics
  9. React Native Async Storage
  10. React Native Share

Error & Issue Solution

  1. Task :app:transformDexArchiveWithDexMergerForDebug FAILED In React Native
  2. Expiring Daemon because JVM heap space is exhausted In React Native
  3. Task :app:transformNativeLibsWithMergeJniLibsForDebug FAILED In React Native
  4. Unable to determine the current character, it is not a string, number, array, or object in react native
  5. App crashed immediately after install react native video or track player
  6. how to delete SQLite database in android react native
  7. React native material dropdown twice click issue
  8. How to get the current route in react-navigation?
  9. how to disable drawer on the drawer navigation screen?
  10. Image not showing in ios 14 react native

Join our email list and get notified about new content

No worries, I respect your privacy and I will never abuse your email.

Every week, on Tuesday, you will receive a list of free tutorials I made during the week (I write one every day) and news on other training products I create.

More articles from Infinitbility

React Native Environment setup on ubuntu

React Native Environment setup on ubuntu

React Native Tutorial - Environment setup on ubuntu

January 7th, 2021 · 1 min read
React Native Environment setup on Mac OS

React Native Environment setup on Mac OS

React Native Tutorial - Environment Setup on Mac OS

January 5th, 2021 · 3 min read
© 2021 Infinitbility
Link to $https://medium.com/infinitbilityLink to $https://www.facebook.com/InfinitbilityLink to $https://github.com/infinitbilityLink to $https://twitter.com/infinitbilityLink to $https://www.buymeacoffee.com/infinitbilityLink to $mailto:[email protected]