Complete guide to structuring large react + redux apps.
How I drastically improved react’s file structure in 7 minutes.
Warning: this tutorial is for advanced react users; beginners might have problems understanding it and I recommend you learn react and ES2015 first (Redux would be useful as well).
I started using front-end frameworks last year. Beginning with polymer, then angular 2, then react and a bit of vue. I found react very useful and the only thing it can learn from angular is how to structure an app. Seriously, who wants to maintain an app that is structured like this:
Please don’t do that. Never. It’s messy overall and painful to navigate as your application grows. If your app looks like this, don’t worry! Don’t switch to angular just to have a different file structure that will be a little easier to read. I will walk you through making your react app file structure great again. At the end of this tutorial, your source folder will look like this:
Currently, my file structure looks like this:
I also made a simple counter app. Purposefully, I didn’t separate it into different files, so I can walk you through and show you how it’s done.
This needs to be separated. Let’s start with the store:
You probably want to separate the reducers from the store file itself. To do that, I will create a new directory called services. This is where the reducers, actions and api’s will be stored.
Inside of the screens folder, I created a new file called router:
The screens folder will contain all the routes, pages, screens, scenes or whatever you want to call them. All you need to know is that the screens can connect to Redux store but don’t have to. Components on the other side should be reusable in other applications, therefore, they shouldn’t directly connect to state. How do you use them then? Simply pass props that contain state from screens.
All screens are currently in the router file, but we would like to separate them. Each screen will be in the screens folder in a sub directory based on its name. For example, a HomeScreen will be in screens/Home.
What about components?
Components are going to ‘live’ in the components folder. Each component can have its own images, sub components and CSS styles. Here is a simple button component:
To use it in the counter screen, it needs to be imported like this:
You can use our beautiful button like this:
But the problem is that those buttons are quite ugly:
To fix them, we would need to use CSS. Create-react-app allows importing CSS, but it’s not using CSS modules, so the CSS used is not scoped. This means that if you apply CSS to one component and you use a class name from a different one, the CSS loader will simply assign both styles to your component, resulting in bugs. But who wants CSS anyway? CSS can’t interact with the state or do as many interactive actions as inline styles.
Inline styles on the other side are more limited. I use styled components - they combine the best of both and allow you to easily integrate interactive and scoped CSS styles to your components. And I know, I probably used the word ‘CSS’ like 20 times in 3 sentences ▲.
This simple button actually looks better than you might think:
But, then you might realise that you actually need global styles. You could create a root component, style it and put everything inside of it( something like a theme provider) but because I will need to import fonts, I will advise you to create a global stylesheet.
I just imported Roboto here and everything seems to look a lot better:
In real life apps, you will need what I call sub screens or sub routes. For example:
To make a sub route, you can’t, for instance, create an increment file or folder inside of the counter screen folder. That wouldn’t be declarative enough. There are reasons for that:
Images / assets
A screen can contain images. If you have an images folder and put sub screens in the same folder, you might think that the images folder is also a screen. Or what if you want to have a screen called images?
Screen scoped components
In some cases, you might want to have a component used just by one screen. There would be no point of putting this component inside of the global components folder, because no other screen would use this. This is when it’s good to use screen scoped components. You just create a new folder called components and declare your components there.
You can do the same thing with components inside of components. In those cases, I call it ‘sub components’ but it’s completely up to you.
If you want to have custom navigation layouts, you need to put them somewhere. Let’s create a new folder called layouts in project root/src.
Remember, everything inside of your layout folder can be connected with state, so technically layouts aren’t stateless…
I have concluded the file structure into this simple description:
components/ -> contains reusable, state free components
SuperButton/ -> component folders must start with a capital letter
images/ -> components can use images
components/ -> components can be extended
layouts/ -> app layout wrapers. examples: navbar layout, single layout, sidebar layout
Default/ -> the name of the layout must start with a capital, as it exports a component
screens/ -> screens that are rendered on different routes.
Home/ -> a screen must start with a capital letter
components/ -> components only available to the home screen.
Welcome/ -> component
index.js -> use index.js instead of Home.js. AirBNB guidelines, not mine.
images/ -> images, svgs and other assets
scenes/ -> a scene can have other scenes inherited
Bar -> /Foo/Bar.
router.js -> assigns a route to a scene
services/ -> redux reducers, actions, apis, function
index.js -> entry point. Where the app is provided with state and rendered.
store.js -> createStore, import reducers, middleware, initial state....
styles.css -> global app stylesheet
If you are still viewing this ridiculously long article, it means you enjoyed it (at least I hope you did). Thanks. I hope you have had a good, productive day.