Updated January 23, 2019
How to Organize Your Components
It can be pretty easy to write code - but how do you organize it?
I've worked with dozens of developers working with React Native and have seen a variety of ways of organizing code. From putting everything into one file to files that have less than 10 lines on average.
In this tutorial I want to give you a balanced approach. A way to organize your code so it's easy to work with and maintain but not a burden to manage/stick to.
Overall Project Structure
We're just covering the UI components in this tutorial but I want to give an overall look at how my React Native projects are typically structured.
Project Directory Structure
/app
/assets
/components
/config
/navigation
/screens
/util
index.js
Not mentioned is the rest of the React Native files. I like to encapsulate my code into an app
or src
directory so I know what's mine all of the time. It also makes it easier to upgrade my project and reduce the likelihood of merge conflicts.
My Component Philosophy
Before going further I want to say that when I'm saying component, in the context of this tutorial, I mean the UI components of my application. They're typically just handling presentation of data and then a different component (my screen) is going to handle that data.
For example: I have a sign in screen with 3 text inputs and a button. I'm going to structure this so that the text input components are displaying the data and handling styling. When the user enters text/presses a button then that will be passed up to the screen to figure out what to do with it.
This is the methodology I take for all my components.
Organizing by "Functional Area"
As your app grows you're going to have more components. As your components grow in complexity you're going to want to break them down into smaller parts so they're easier to reason about and maintain.
As this happens you could end up with a huge components directory! This can be fine - even in my example below I think it's perfectly fine.
Big List of Components
/components
TextInput.js
Switch.js
Button.js
List.js
ListItem.js
Loading.js
Calendar.js
CalendarItem.js
CardContainer.js
CardBodyImage.js
CardBodyText.js
Header.js
HeaderLeftButton.js
HeaderCenterContent.js
HeaderRightButton.js
...
But it can be difficult to figure out how they all relate to each other. Imagine you have shared styling between your card components, header components, form, list, etc. Where do you put those shared pieces of logic?
This is why I like to break components up one level deeper by functional area. Let's take the example above and organize it one level deeper.
Components Organized by Functional Area
/components
/Form
TextInput.js
Switch.js
/List
List.js
ListItem.js
/Calendar
Calendar.js
CalendarItem.js
/Card
CardContainer.js
CardBodyImage.js
CardBodyText.js
/Header
Header.js
HeaderLeftButton.js
HeaderCenterContent.js
HeaderRightButton.js
Loading.js
Button.js
We've added one level of nesting but it helps organize components by functional area. We know what's related at a glance.
Now if we need shared styles between a set of components we just put it in that directory. Easy.
One last thing - I like to make a component functional area export an API. It let's me rename/restructure things at will. That means I add an index.js
to each directory that exports the components.
Form/index.js
import TextInput from './TextInput.js';
import Switch from './Switch.js';
export { TextInput, Switch };
The other benefit of this is that my imports from other screens/components are reduced. Rather than having to do a
import TextInput from '../components/Form/TextInput';
import Switch from '../components/Form/Switch';
I can have one import
import { TextInput, Switch } from '../components/Form';
Avoid Deep Nesting Content
Now a quick caveat to this approach. I would highly suggest you don't go deeper in nesting than this. It can turn into a major pain point beyond this.
And if it's painful you're not going to stick with it everywhere which causes inconsistency. Inconsistency causes confusion. Confusion causes skynet. Don't create skynet.
Here's an example. Let's say we've got a set standard colors in config/colors.js
. To get that from our TextInput.js
file would look like
TextInput.js
import colors from '../../config/colors.js';
I'm fine with that. But if we went deeper you start getting more and more ../../../..
.
The biggest challenge of this is just looking and seeing how many directories you have to go up. Two I can grasp easy. 3 and I have to start counting.
Be wary of deep nesting.
Stay Flexy
To wrap this up I just want to remind you to stay flexible. What works for me won't work for you 100%. Stay flexible. Sometimes it makes sense to put a functional area into a directory (a form, for example). Other times there's no reason (like a loading indicator). Stay flexible and just establish systems/rules in your app and adhere to them.
And when you outgrow them then revist them. You'll have definitive experience and know why something isn't working so you can then go ahead and fix.
Don't spend a ton of time worrying about the "perfect" code organization. I guarantee you'll end up hating something about it whether you spend 10 minutes or 10 hours setting it up.
Want to go deeper on creating a great component library to build your app on top of? Check out my "Building a Component Library with Storybook" class on React Native School! We cover setting up and using Storybook, breaking an interface into components, how to build them, and more!