Atomic Design for Developers: Atomic Engineering
In my first article on Atomic Design for Developers I discussed a good way to organize components in your project. I highly suggest reading it first, although I'll briefly summarize it as well. If you don't know what atomic design is, please read Brad Frost's article on it . So, the big question left after Atomic Design for Developers: Project Structure is how do we actually implement atomic design in practice? First we'll do a quick review for new readers on atomic design, but those who read the last article can skip down to the Building the Google Search Page section. Atomic design is Brad Frost’s methodology for building design systems. The idea is that we can take the basic building blocks of living things and give our UI a hierarchical structure based on it. Brad Frost defines five main stages of UI components: Atoms are the simplest form of UI, consisting of things like headers, labels, input fields, buttons, etc. Molecules are a combination of atoms that form more complex pieces of our UI, such as a search field with a submit button. Organisms build on top of molecules and orchestrate larger parts of the UI. This can include a list of products, a header, forms, etc. Organisms can even include other organisms. Templates are where our pages start to come together, giving context to all of our organisms and molecules by giving them a unified purpose. For example, a template for a contact page will have organisms for headers and forms, and molecules for text fields and navigation bars. Pages , as the name implies, is our final page with all its content. The difference between pages and templates is that templates don’t provide any content. As a way to demonstrate atomic design in practice, I created a painfully simple rip off of the Google search page. You can type a query, click search, redirect to /results page, and see a list of hard coded results. The article will focus on the high level principles, and the sandbox project will give a good reference for how to use those principles in practice. Let's pretend that our design team just passed us a couple of mocks that look like these: Okay, so just by looking at the mocks we can begin to think about how the page elements would be categorized into atoms, molecules, organisms, and templates. I find it helpful to start from large components, then see how we can break them down into smaller ones. First, let's take a look at the app bar. The app bar is a fairly complex component. We have a profile image, menu items, and one of them is even a dropdown menu. Because our app bar is orchestrating smaller elements, the app bar is a good candidate for an organism. What about our profile image, images link, and gmail link? These definitely qualify as atoms. They can't really be broken down further. That just leaves the "me" dropdown menu. This is a fairly complex component compared to our links and profile image. Organisms can be composed of other organisms, so it's okay to categorize our dropdown menu as an organism as well. Our search area is pretty simple. It's just the Google logo, an input field, and a button. These would all be atoms by themselves, but we should pay attention to how elements relate to each other. For example, look at the relationship between the search input and the google search button: As a whole, we don't see a complex management of components that we would expect from an organism. Molecules give a little more context to the relationship between atoms, and tend to search one specific function. This is why we will categorize the search bar + google search button as a molecule. So the part that says "Results for: awdwadwa" is just a header. I'd just classify this as an atom and move on. The more interesting part is the search results. Given that each search result consists of an icon, a header, and a description, we may classify this search result item as a molecule. What happens when we are managing a list of results? Well, now we are back in organism territory. We might have an organism called ResultList that deals with laying out our results. In summary, here is how we categorized our page elements so far: What about our templates? Well, that should be pretty easy. They are just the skeleton of the mocks that we were given. In other words, it's the page without specific data. Okay, so far we have looked at everything from a design standpoint. We broke down the mocks our design team gave us, but it's still unclear how we actually want to build out our components. Here is a diagram giving the atomic design stages a little more engineering context. In general, I would suggest starting from the top of the hierarchy and working your way down (templates -> organisms -> molecules -> atoms) unless you are absolutely positive which category a component belongs to. The smaller your components get, the more defined their role becomes. For example, remember how we came up with a SearchField molecule in the mocks? Take a look at molecules/SearchField in the example project. While it is a valid molecule, did it really make sense to create one? We can only use it in one place in our app (strict styling & functionality), and the atoms that make up our SearchField can easily live in isolation. It's hard to avoid, but we can minimize the amount of refactoring and re-categorizing of components by starting from large to small. This can be tricky. Atomic design principles differentiate the two by saying organisms are relatively complex compositions of atoms and molecules, and molecules are simple compositions of atoms. How can we differentiate between the two categories in code? Here are a few tips to help you figure it out. The definition of molecules and organisms can get a little foggy in practice, but you want to make sure there is a clear line between pages and templates (and then rest of your components for that matter). Storybook is a good tool to verify that you are on the right track, and I'll talk more about it later. If your template can't be presented in a story without hooking up providers, stores, etc, it's a sign that you are doing something fishy. Pages make your web application come to life. Updating state based on API calls? Do that here. Pulling in images from your cloud storage service? Do that here. Using a HOC from Redux or React Router to get access to state or the history object? Wrap your page and pass it down. Don't wrap your templates or other components. It's kind of like how we isolate our APIs from our frontend. We are just taking that a step further and isolating our UI from our app state. That may not be your goal, but you will want to think that way, and here is why. Damn right buddy. All that effort spent isolating your UI components from your application logic has made what is often times a massive team effort into a day's worth of work. The second your design team turns around and tells you they want to build a component library + design system, you can suavely take your sunglasses off and say, "way ahead of you pal". Even if there is work to do on the components themselves, whatever exists won't break, and you can rest assured that breaking changes will be due to implementation details and not package dependencies. You can organize your storybook the same way you organize your components. This is great for both you and your design team and makes collaborating easier. You shouldn't have to worry about components breaking due to dependency issues because you built them to be as independent as possible. For storybook users, how many times have you had a story fail to compile because you didn't wrap a component in some kind of provider? When you move storybook into a new repo with your components, once again, everything will just work ! If it doesn't work because your template component depends on React Router instead of being passed router props from the page, then Storybook will let you know that very fast. How many times have you had failed tests because your component requires some HOC or injected dependency from another library? Could be Redux Form, redux itself, react router, or anything at all. Atomic engineering is really just an enforcer of dependency injection. In the example project, when we type a query and click search, the app goes to the /results page using react router's history object and then displays the results. If you look at the SearchField component in the example project, it doesn't care about the actual routing aspect. All it cares about is being given an action to perform on search. So when we test it, we can easily mock an onSearch function. This isn't a new concept. Atomic engineering simply helps you enforce this more easily. 90% of startups fail. Many are forced to pivot their product direction, but cannot stay alive long enough to do so because they had to completely scrap their original product and build a new one. Everything in the /components folder can be leveraged on your next project (except templates because of their context specific nature) with little to no setup needed, even if you aren't building a "component library" per-se. Please take a look at the example project and read through the comments. Even though the application is dead simple, it will give more context to the things I'm saying. If you have questions, personal experiences you wish to share, or anything else, please leave a comment with your thoughts! This isn't a bullet proof method, but I think it's better than the traditional container vs component approach.