Better Way to Structure React Projects (Part 2)
9 min read
In part 1 of this series, I talked about how to use the atomic design methodology to structure your React components.
It's safe to say that there was a lot of controversy around it.
- This is nice on small projects but it doesn't scale.
- I would go nuts trying to differentiate between an atom, molecules, organism, etc.
- This is a design methodology, it doesn't work well in development.
All fair points. I purposefully didn't go into much detail in part 1, so if you had any of the concerns mentioned above, today is the day I address them.
Objection 1: This is nice on small projects but it doesn't scale.
A big concern is that many components either don't nicely fall under an atomic category, or are so domain specific that they can only be used in one place.
Probably not often.
The same applies to an atomic design folder structure. You have to adjust it to your specific needs.
Here are two ways I have used it on existing projects:
Option A - Add a "components" sub folder in each page
You might be hesitant to add yet another components folder, but in practice I found this to be very intuitive for developers. You have a "shared" components folder at the root. You can even rename your root components folder to "shared" if it helps separate concerns a bit more. The new components folder, specific to each page, contains one-off components with a lot of business logic that feel doesn't make sense to put in a shared folder.
A benefit of this approach is that it's easy to take your "shared" components folder and turn it into a component library with very little additional work.
Option B - Add a "domains" folder
This structure works particularly well for Next.js applications, since you can't add components to the pages folder without them being detected as actual web pages.
Objection 2: I would go nuts trying to differentiate between an atom, molecules, organism, etc.
This can get very subjective, but like any other organizational decision, how each level of components are differentiated should be agreed upon by the whole team.
It's okay if your definition of an atom and a molecule don't exactly match what Brad Frost defines as an atom and a molecule. Define it in a way that makes sense to you, your team, and your project.
How I categorize components changes from project to project, but here is how I approach it in general:
The smallest component I can create. Typically these are Buttons, Icons, Typography, Images, Text Fields, etc. It isn't very hard to determine what would make an atom. If you can't break a component down any further, you have an atom.
Components that are a relatively simple combination of atoms. They should have a very specific responsibility. Here are some examples:
- A search field and submit button
- A multi select dropdown
- A product list item with an image, title, price, and description (or any kind of list item)
- A snackbar component with a message, icon, and close button
- A card component
organisms and molecules are the hardest to differentiate. I personally identify organisms as orchestration components. They tend to be more abstract, and control how molecules are laid out and interact with one another. This includes things like headers, sidebars, modals, forms, grid layouts, lists, etc.
In a simple world, you would keep application state out of organisms and let pages feed the actual data, but I found that this only works for very simple apps.
With that being said, this is typically the lowest level of components where I start injecting app specific state in order to keep a balance between performance and organization.
Truthfully, I don't use this folder very much in practice on large projects. It can be very handy when you are using tools like storybook and want to mock entire parts of the application, or if you have an app with a relatively simple state structure, but I haven't found much value outside of that. By definition, you wouldn't want these components to manage any application state since that would defeat the purpose of it being a template. Consequently, it's hard to justify this additional layer on large projects, but if anyone has suggestions please share!
This is one is usually clear cut. One thing I'll say about it is that if you can keep all of your business logic at the page level, then more power to you. It makes maintenance extremely easy, since your application state is in one place. However, this is pretty hard to implement in large, data driven applications.
My advice would be to try and keep your business logic at the page level for as long as you can, and only move things down layers when it's absolutely necessary. This should keep you from pre-maturely optimizing.
Objection 3: This is a design methodology, you can't use it in development.
... what? We can't draw inspiration from places outside of our domain?
Atomic design is a mental model that applies directly to Component Driven Development principles. In CCD, we want to start with the smallest, simplest components and build our way up until we get our end result: pages.
Atomic design encourages you to think in terms of composition. It's not a rigid set of rules. You can just as easily create some kind of analogy to legos, an orchestra (my personal favorite), or sports. Whatever floats your boat and gets the point across.
To say we can't use atomic design as developers because it's a design methodology just sounds lazy. If we as developers are doing our jobs right, we are designers as well, just at a different stage of the system..
I hope this cleared up some doubts around the use of atomic design in a development environment. I'm not saying atomic design is the all encompassing solution to your code organization problems, but it's definitely a handy tool to have in your toolbox.