In this section, we'll cover project organization and separation of concerns within components at a high level.
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.
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.
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
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
useReducerhook (or another library)
theme: shared colors and text styles (sometimes called
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 └── ...
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
apps directory. Components or utilities that are shared between multiple features/teams will often be in a special
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 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
Image - that's the responsibility of 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.
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
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?
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!