<aside> ⬅️ For previous article in the series, see Starting new side project ... for a few times.

</aside>


You are starting a new project, and you know the drill. You usually have some basic template provided for you, in this case provided by the expo init my-app command. But how to structure your project files from there? I was just about to wing it and dive head first into creating code, but then a weak nagging feeling of β€œthis is not the way” creeped up on me πŸ˜„. So I attempted to do a little research. The first few articles (Best Folder Structure for React Native Project, An efficient way to structure React Native projects) talked about grouping components according to categories from Atomic Design. This seemed interesting, but then I stumbled upon an article that seemed to go a little deeper.

Grouping by type vs feature

I won’t rewrite everything here, but the premise is that grouping by feature is more natural, and it also offers benefits such as increasing co-location of code and making refactoring easier. This seemed to ring true to me, and it was also different from the grouping by type that I was used to from other projects.

Handling common code

Of course, you will have some common code that will be used between more components. The solution to this is a separate common directory. For shared React components we can even use the previously mentioned Atomic Design principles to flesh out important shared details with more structure.

Example of an project with this structure might look like this:

src
β”œβ”€β”€ common
β”‚Β Β  β”œβ”€β”€ components
β”‚Β Β  β”‚Β Β  └── LoadingScreen.tsx
β”‚Β Β  β”œβ”€β”€ constants.ts
β”‚Β Β  β”œβ”€β”€ helpers.ts
β”‚Β Β  β”œβ”€β”€ hooks
β”‚Β Β  β”‚Β Β  └── useIsMounted.tsx
β”‚Β Β  β”œβ”€β”€ layout
β”‚Β Β  β”‚Β Β  └── SafeAreaLayout.tsx
β”‚Β Β  β”œβ”€β”€ navigation
β”‚Β Β  β”‚Β Β  └── RootNavigator.tsx
β”‚Β Β  β”œβ”€β”€ state
β”‚Β Β  β”œβ”€β”€ theme.ts
β”‚Β Β  └── types.ts
└── screens
    β”œβ”€β”€ settings
    β”‚Β Β  β”œβ”€β”€ component
    β”‚Β Β  β”‚Β Β  └── SettingsComponent.tsx
    β”‚Β Β  β”œβ”€β”€ SettingsScreen.tsx
    β”‚Β Β  └── state
    β”‚Β Β      └── settingsAtoms.ts
    β”œβ”€β”€ today
    β”‚Β Β  β”œβ”€β”€ actions
    β”‚Β Β  β”‚Β Β  β”œβ”€β”€ completeTask.ts
    β”‚Β Β  β”‚Β Β  β”œβ”€β”€ createTask.ts
    β”‚Β Β  β”‚Β Β  └── planHabits.ts
    β”‚Β Β  β”œβ”€β”€ components
    β”‚Β Β  β”‚Β Β  β”œβ”€β”€ NotionTaskCard.tsx
    β”‚Β Β  β”‚Β Β  └── TaskCard.tsx
    β”‚Β Β  β”œβ”€β”€ constants
    β”‚Β Β  β”‚Β Β  └── taskContants.ts
    β”‚Β Β  β”œβ”€β”€ state
    β”‚Β Β  β”‚Β Β  β”œβ”€β”€ habitsAtoms.ts
    β”‚Β Β  β”‚Β Β  β”œβ”€β”€ tasksAtoms.ts
    β”‚Β Β  β”‚Β Β  └── timeBlockAtoms.ts
    β”‚Β Β  β”œβ”€β”€ TodayScreen.tsx
    β”‚Β Β  └── todayTypes.ts
    └── wakeUp
        β”œβ”€β”€ actions
        β”‚Β Β  └── createDailyQuest.ts
        β”œβ”€β”€ state
        β”‚Β Β  └── dailyQuestAtoms.ts
        β”œβ”€β”€ svg
        β”‚Β Β  └── SunriseSvg.tsx
        β”œβ”€β”€ WakeUpScreen.tsx
        └── wakeUpTypes.ts

Bells & whistles

Getting rid of relative paths

One of the other things mentioned more times was setting up your project so that import paths would not be relative, but absolute. The difference is:

import { RootStackParams } from '../../library/navigation/RootNavigator';
import { SettingsComponent } from './component/SettingsComponent';

vs this:

import { RootStackParams } from 'library/navigation/RootNavigator';
import { SettingsComponent } from 'screens/settings/component/SettingsComponent';

At first I looked at this as a cosmetic issue, thinking β€œWho the hell writes imports by hand? You just press enter for automatic import in your IDE and you’re golden”. But after a few days it clicked, when I came upon a concrete example. Suppose you have the folder structure described above, with a library for common code, and a separate directory for each feature, and that your feature folder has a component name that overlaps with that of common component. Of course you should strive to name things uniquely to avoid this confusion, but it happens. With relative imports, you are faced with the question, β€œam I importing what I think I am?”

import { SettingsComponent } from './component/SettingsComponent';
import { SettingsComponent } from '../../component/SettingsComponent';