When I first started programming, I really had no idea what abstract programming concepts like design patterns or file structures were or why they were so important until I wrote my first application for a hotel. The application was to help the hotel print receipts for their respective customers. A year later, I was asked by the hotel management to implement some new features for the application. Unfortunately, I ended up having to rewrite the whole application from scratch because I had initially structured the application so poorly, it resembled the windows mine-sweeper game I played when I was younger. Any small step would lead to a blow-up. This happened again when I got called to introduce more features and after two re-writes, I abandoned supporting the project. I paid someone else to rewrite it for the hotel. Nowadays, I look back at the previously implemented code with complete disdain and regret putting the client in such a bad position.
Firstly, I would like to give a shout out to Brad Frost for introducing the Atomic Design principles. It honestly changed my coding journey by making me see problem-solving in a more holistic manner.
The aim of the article is to help people understand and discover a different way to structure our frontend applications. Atomic design is a concept of breaking user interfaces into smaller simpler elements which ultimately helps to create more consistent UI with better maintainability.
Componentization in React
I think one of the exciting concepts that React has brought into the eco-system is componentization. However, in a lot of React projects I have viewed, a lot of developers fail to take full advantage of this concept and repeatedly violate the DRY principle. This is possibly due to the absence of standard best practices for design patterns in the React Docs as most codebases are built upon the opinions of the individuals contributing to it. The React Docs do, however, recommend composition with a really good example that demonstrates different levels of component specialization.
These concepts can be applied to most frontend frameworks but I chose React as that is what I am most comfortable with (I am also aware Facebook calls it a UI library, but it’s a discussion for another article).
React does(Docs)also recommend a design-first approach for frontend development – In this approach, a UI/UX designer team would come up with mocks for the developer team to implement, The team can now go about breaking the UI mocks down into a component hierarchy. Breaking down components based on some sort of hierarchy is one of the foundations when implementing Atomic design. Atoms, Molecules, Organisms, Templates, and Pages help build the hierarchy.
But Why Atomic Design?
When I first started using this methodology, it was with a large React Single Page Application (SPA). We had a lot of custom components to create and this wasn’t a solution we could find when looking at present CSS frameworks and libraries. So the result was having to create these components by building them with HTML elements and using custom CSS. The concept of having one location with all the components was actually quite interesting and it actually led to faster development times.
By borrowing this terminology from Chemistry, Brad Frost introducing the Atomic Design concept and utilizing it with UI components makes sense. As the building blocks of websites are HTML elements and just like atoms these elements are combined together to form the complex pages. Each page designed by developers can be broken down into it’s smaller constituent components, similar to what is taught in Chemistry with molecules and organisms.
Atomic design is atoms, molecules, organisms, templates, and pages concurrently working together to create effective interface design systems.
Atoms are the smallest possible components, such as buttons, titles, inputs, text. Atoms of our interfaces serve as the foundational building blocks of our components and can’t be broken down any further without ceasing to be functional. Molecules as they are named consist of two or more atoms, molecules are relatively simple groups of UI elements functioning together as a unit. Examples are A Textfield comprising of an HTML textInput, a label, and an error message or a search Box comprising of an HTML TextInput and a Button. Organisms are relatively complex UI components composed of groups of molecules and/or atoms and/or other organisms. These organisms form distinct sections of an interface.
Templates place components within a layout and demonstrate the design’s underlying content structure. They are basically a skeletal structure of what the page would look like without the components of the page. Pages are specific instances of templates that show what a UI looks like with real representative content in place. Templates and Pages (pages are just instances of templates) also contain organisms, molecules, and atoms. The joining together of these smaller components makes up our user interfaces in our applications.
So using the components discussed earlier, we can flesh out a starting folder structure for an imaginary codebase. Remember the folder positions could vary across codebases but it is vital to discuss this with your team before implementing. In the screenshot above, I have a Components folder to house all our components used on the frontend, I have a UI folder to separate my smaller design components from the page-level structures.
I guess one of the recurring questions I got from other members of my team was what to do with components that use an internal state since this state could be reliant on the value of business logic supplied to it. It is important to distinguish this internal state from the business logic of your React application. A good example is the open and closed state of a dropdown button, this is a good example of an internal state of a component. With this approach, our components are basically like dummy components which are only updated depending on the props you update the components with.
Components can be developed separately from the application, tested and viewed on tools like a style guide before importing them into your application. This also means there is no over-reliance on back-end application logic for starting front-end development.
Once a set of patterns has been established, we can have a faster build process, with more flexibility in case a change needs to be made to the designs. Designs can be more consistent since we are re-using a lot of existing components.
This pattern also helps us manage our CSS a lot better since our CSS is tied to specific components. So depending on the architecture of your application you should only render the CSS utilized by the components rendered.
Media queries do become a bit harder as when components in isolation you have no way to determine the size of parent containers. Components have no knowledge of their width so resizes happen in response to changes to the actual page sizes.
This solution can be remedied by introducing layout components that would surround your components and resize them accordingly. These layout components would implement CSS layout properties like flex, grid, etc.
At the moment, from our screenshots above, we just have our app existing just with its components. What we need is a reliable way to bring in our business logic to update these components. I think it is good practice to keep your business logic away from your views (separation of concerns). This way it is easier to debug problems with code especially when your project starts to expand. We can achieve this by adopting different approaches. I personally like to use Higher-order components(HOC) and populate the pages with any state using props to the specific page. This state could be a series of API calls shared around the application. Other solutions could involve the render props pattern or react-hooks.
A more abstract explanation of this is could be to imagine your components were empty bottles of the same color and size as the image below. You decided to fill each bottle liquid of a different color. This could be likened to you updating your state of different components but in case your state is the liquid and the bottle is your component. Filling each empty bottle with a different color of liquid would be a more straightforward process and if for any reason you need to update the state by changing the color, it would be a much easier process.
However, If I decided to have my bottles half-filled with a red color liquid and then decided halfway that I wanted to fill it up with a different color then I would have a bit of an issue. What if they don’t mix? Or What if I had to remove the original color and replace it with a different color ?. This is exactly the scenario that happens when you have a view already implementing business logic, it makes it harder in the future to refactor code and also harder to re-use the view
Here is what I came up with…
Even though React is unopinionated it recommended you limit yourself to a maximum of three or four nested folders within a single project. However, you do have alias built into both jest, webpack, and babel to help resolve relative imports so your imports don’t look overly clunky.
For lovers of Redux 😛, there is this lovely article by Katia Wheeler that should set you on your way. It is located in the references.
I would also like to thank my former colleague and frontend Maestra Ugonna Thelma for a good example of how to practice this in a project please check it out. Special thanks to Chidiebere Japheth Anyigor, Shehu Muhammad and Segun Ola for helping me review this article
http://atomicdesign.bradfrost.com — Brad Frost
https://medium.muz.li/atomic-design-methodology-166261ce47c2 -Harshani Chathurika
https://codeburst.io/atomic-design-with-react-e7aea8152957 — Danilo Woznica