Project Structure

    In this section, we'll cover project organization and separation of concerns within components at a high level.

    Project organization

    There are many valid ways to organize a project. Here we recommend a few common approaches for projects of different sizes, but if you're already familiar with web development, you can organize your project the same way you would for a web app.

    Small projects

    When our app is small, we often keep all components in a single directory:

    MyApp
    ├── components
    │   ├── Avatar.js
    │   ├── Button.js
    │   └── List.js
    └── App.js

    Generally, each component in our app should live in a separate file, and should be the default export of that file. We give the file the same name as the component, e.g. a component called Avatar should live in Avatar.js. In React Native, component names must be capitalized, so the file name will usually be capitalized too for consistency.

    If you're starting a brand new app and you're not sure how large it's going to be, starting here and letting it grow naturally is fine! If you know it's going to be a massive app (e.g. Facebook, WeChat) with multiple teams of developers contributing code, consider choosing the large project structure to begin with.

    Medium projects

    As our app grows, we'll typically separate "screen" components into a screens directory, and start further categorizing files within the components directory. A "screen" component should take up the full screen, such as a Profile screen or a Settings screen.

    If we're using a library like react-navigation, we may organize our navigators/routers into a separate navigation directory (also sometimes navigators or routing).

    MyApp
    ├── components
    │   ├── buttons
    │   │   ├── RoundButton.js
    │   │   └── SquareButton.js
    │   ├── cards
    │   │   ├── ArticleCard.js
    │   │   ├── ImageCard.js
    │   │   └── VideoCard.js
    │   ├── Avatar.js
    │   └── List.js
    ├── screens
    │   ├── Feed.js
    │   ├── Search.js
    │   ├── Post.js
    │   ├── Reply.js
    │   ├── Profile.js
    │   └── Settings.js
    ├── navigation
    │   ├── RootStackNavigator.js
    │   └── ProfileTabNavigator.js
    └── App.js

    A few other common categories of file:

    • api: network API calls, often organized by provider or route
    • assets: images and other files to bundle with the app
    • hooks: custom hooks
    • reducers: reducer functions, for managing app data with the useReducer hook (or another library)
    • theme: shared colors and text styles (sometimes called styles)
    • utils: miscellaneous tools like string formatting
    MyApp
    ├── api
    │   ├── twitter.js
    │   ├── facebook.js
    │   └── instagram.js
    ├── assets
    │   ├── app-icon.png
    │   └── splash-screen.png
    ├── hooks
    │   ├── useInterval.js
    │   └── useLogin.js
    ├── reducers
    │   ├── posts.js
    │   ├── users.js
    │   └── tweets.js
    ├── theme
    │   ├── colors.js
    │   ├── textStyles.js
    │   └── spacing.js
    ├── utils
    │   ├── generateUuid.js
    │   └── formatCurrency.js
    └── ...

    Large projects

    When a project grows to include many different features or UI flows, it's common to categorize files by feature at the top level. If multiple teams are working on the app, the feature names will frequently align with the team names: e.g. accounts, growth, privacy.

    The feature directories are commonly grouped under a modules, packages, or apps directory. Components or utilities that are shared between multiple features/teams will often be in a special shared or core directory within that, and treated as a public API. A feature should only reference files within its own directory or the shared directory, never a file within another feature directory — that's a sign that the file is a candidate for moving to shared and considering it a public API.

    This project structure aims to clearly describe for each file: where it should live, who maintains it, and whether it's allowed to be imported into other features.

    MyApp
    ├── modules
    │   ├── accounts
    │   │   ├── components
    │   │   │   ├── UserProfile.js
    │   │   │   └── LoginInput.js
    │   │   ├── screens
    │   │   │   ├── Profile.js
    │   │   │   ├── Login.js
    │   │   │   └── Deactivate.js
    │   │   ├── utils
    │   │   │   └── formatAccountName.js
    │   │   └── App.js
    │   ├── growth
    │   │   ├── components
    │   │   ├── screens
    │   │   ├── utils
    │   │   └── App.js
    │   ├── privacy
    │   │   ├── components
    │   │   ├── screens
    │   │   ├── utils
    │   │   └── App.js
    │   └── shared
    │       ├── components
    │       │   ├── Avatar.js
    │       │   ├── Button.js
    │       │   └── List.js
    │       └── utils
    │           └── format.js
    └── App.js

    Separation of concerns in components

    In React Native, the component is the only building block of our UI. Some frameworks separate concepts like "views" and "controllers", but React doesn't enforce any specific pattern. It's up to us to decide what abstractions we want.

    One common abstraction is to separate presentational and container components.

    Container components

    Container components contain application/business logic. They're usually just called "containers". You may also hear "smart components", or occasionally, "view controllers" (although they may not be totally analogous to traditional view controllers).

    Containers are aware of the data and logic unique to your application. Containers pass data and callbacks as props to presentational components, and handle updating the data when a user interacts with the app. Containers shouldn't render core components like Text or Image - that's the responsibility of presentational components.

    Presentational Components

    Presentational components render the visible UI, and should not contain any application-logic. Ideally, their only input is their props, so they could be used in a different app without any major modification. Presentational components are often referred to as "components", in constrast with "containers".

    If your company has multiple apps, it may be valuable to share code between apps. Typically, you would start by sharing presentational components between apps, since they're already separate from any application logic, and thus sharing them should be relatively easy.

    Example

    In the medium-sized project example file structure above, our screens directory contains our "container" components, while components contains our presentational components. If we wanted container components that didn't represent entire screens in the app, but rather portions of a screen (that might be shared between different screens), we might additionally make a containers directory.

    MyApp
    ├── components
    │   ├── buttons
    │   │   ├── RoundButton.js
    │   │   └── SquareButton.js
    │   ├── cards
    │   │   ├── ArticleCard.js
    │   │   ├── ImageCard.js
    │   │   └── VideoCard.js
    │   ├── Avatar.js
    │   └── List.js
    ├── containers
    │   ├── CardList.js
    │   ├── UserProfile.js
    │   └── RelatedTweets.js
    ├── screens
    │   ├── Feed.js
    │   ├── Search.js
    │   ├── Post.js
    │   ├── Reply.js
    │   ├── Profile.js
    │   └── Settings.js
    └── ...

    Want to learn React Native in-depth?

    If you like React Native Express, you'll love my new book, Fullstack React Native: The complete guide to React Native! Throughout the book, we'll build 7 full apps, covering complex topics like navigation, gestures, and native modules. We don't assume any knowledge of React or newer JavaScript language features, so you can dive right in regardless of your experience level. The book comes in PDF, EPUB and MOBI formats.

    Looking for more help?

    Infinite Red sponsors React Native Express and is the premier React Native agency. They're also the team behind the React Native newsletter, podcast, and conference listed here. Get in touch at infinite.red/react-native for a proposal on your next project!