Organizing a React Native Project
The following is an excerpt from my upcoming course on building apps with React Native & Meteor. If this sounds interesting to you, sign up for my email list.
One of the great things about React Native is the flexibility. You can do just about anything — including organize the project however you want. This is great! But can also be daunting, especially to new developers.
There are numerous ways to organize a project and you can spend a lot of time reading about them and debating the pros and cons of each (none will be perfect). I want to introduce you to one, the one I use. It’s only one way and I’ve found it to work over time and stay simple to understand.
If you don’t feel like reading each section includes a video covering the same topics.
There are a few primary goals I have when organizing a React Native project.
- Maximum Code Reuse — Much like being able to write for both platforms I want be able to reuse as much code as possible between the two platforms. Even if the UI components look different between platforms the business logic behind them should be the same and reused.
- Keep Configuration out of the Code — Settings, colors, image paths, routes, etc. are all pieces of code that are used in multiple places throughout the app and can change. I want to make it easy to change these items without worrying about if I changed everyone of them or if I broke anything. These items should live in one easily accessible place.
- Minimize Component State — I wan to use stateless functional components, components that are a function of their props, as much as possible. It keeps things simple, I’m a firm subscriber of KISS (Keep it Simple, Stupid).
Understanding the Default Structure
Before diving into our customizations let’s do a quick overview of what you’re getting by default. This will help you understand what we’re working with and what everything means.
- android/ — This is the directory where all of the native Android code lives. If you dive in there you’ll find .gradle files, .java files, and .xml files. This is the directory you would open with Android Studio. You’ll rarely have to work in this directory.
- ios/ — Like the android directory this is where all of your native iOS code lives. You’ll find your xcode project in there, .plist files, .h files, .m files, etc. So if you want to open your project in xcode you would open ios/<PROJECT_NAME>.xcodeproj. You’ll rarely have to work in this directory.
- index.ios.js — This is the entry point for your ios app into the React Native code. It’s where you’ll want to register your app (via AppRegistry).
- index.android.js — Same as index.ios.js just for Android. You’ll notice that it’s exactly the same code as the ios one, we’ll work on that in the next section.
- Everything else — You probably won’t have to worry about the other stuff — it’s mostly configuration for React Native and the rest is your standard node files/directories (node_modules/ and package.json).
The app/ directory
So to go from the default file structure to a point where we’re completing our goals we’ll need to figure out a single entry point for our entire app, that’s where our app/ directory will come into play. Here’s an idea of where will be going.
So everything should look pretty standard (this is just a subset of the entire tree), the only difference being the addition of the app/ directory which has 6 directories and one file. What do they each mean? Let’s run through them.
This directory aligns with our goals of Maximum Code Reuse and Minimize Component State. In this directory we’ll be writing mostly functional components that can be used in multiple places throughout our app. Here’s an example of what a components/ directory looks like (examples pulled from my React Native Meteor Boilerplate).
A little explanation as to what’s going on here. The index.js files manages what is exported from that directory, it’s the only thing we require from outside of that directory. Avatar.js is the actual component (it’s a functional component) that displays our data. Lastly is the styles.js file — this keeps the styles for the component directory.
Now this may seem overkill for such simple items but I’ve found that as a project grows following this structure keeps things easy to understand and consistent.
Why keep styles outside of the component file? The purpose there is that as you break a larger component into smaller ones (like in GenericTextInput) you’ll reuse a lot of the same styling. And remember, we want to maximize code reuse.
This directory aligns with our goal to Keep Configuration out of the Code. Like I mentioned earlier we’re going to keep anything that is used in multiple places throughout our app in one easy to access (and change) place. Here’s an example, again pulling from my React Native Meteor Boilerplate.
You can see here that we’ve got an images.js file which manages paths to various images in our app, routes.js file which gives us a single place to access all of our routes, settings.js file which has info such as our server URL, and finally styles.js file which has global colors in it.
This gives us one easy place to go to to manage various configuration items. It’s also nice for when people who aren’t familiar with the code (such as designers) to jump in and swap out an image.
This isn’t very intimidating and there’s no chance of messing up the logic of your app.
Short section here — it again aligns with our goal to Maximize Code Reuse and, to some extent, Keep Configuration out of the Code. This is where I keep all images for my app. It’s simple to swap out and not intimidating (for example if I’ve got a designer who wants to swap out the logo). Here’s the tree from the boilerplate.
The layouts directory doesn’t directly align with any single goal but the purpose here is to give us an idea of different experiences a user may expect in our app — it’s mostly for developer happiness and ease of understanding. For example an app may have an Auth layout, an Admin layout, and a general App layout. The directories themselves are organized very similarly to the app/components/ directory.
In a layout I’ll normally setup the global navigation for that layout (like tabs or a menu).
This directory aligns with our goal of Maximum Code Reuse and Cross Platform. We’re just keeping any general functions in here. In reality anything you think you’ll put in the app/lib/ directory can probably be found as an npm package.
This is the bread and butter of the app and aligns with our goal to Maximize Code Reuse and write Cross Platform. Typically an app is made up of a variety of routes (or screens/scenes, depending on your preference). The app/routes/ directory is setup very similarly to the app/components/ directory with one difference and that’s the Container components. This is a component that fetches and manages data for that route/screen/scene. We can also keep any route specific components in this file. I like to colocate code as much as it makes sense to in order to keep it easy to know where to look and understand the ramifications of changing a component.
Here’s the tree from my routes directory:
Finally is the only file in our app directory and that serves as our entry point into our Cross Platform app. It allows us to require a single file from both index.ios.js and index.android.js and show the same thing on both platforms.
One thing that I want to drive home is that, in some way or another, each directory is contibuting to writing a Cross Platform app, app/index.js just serves as the starting point. If you want to learn more about sharing code between Android and iOS check out this article.
Expanding on the app/ directory
What I ran through before is the most simple version of this pattern. If you’re building a larger app you very well may be using other technologies as well— such as Redux (which I do).
If that is the case it’s easy to extend this pattern. For the Redux example I add a few more files/directories to my app.
- app/actions — I keep my redux actions here.
- app/reducers/ — Redux reducers live here.
- app/config/store.js — Setup my Redux store here.
I hope you found this beneficial and that it provided a good understanding into the why behind the decisions I make in my project organization.
If you enjoyed this and would like access to my upcoming course on building apps with React Native and Meteor sign up for my email list here.