Cover image for this blog post

Atomic design principles applied to React 💗

By Prasath Soosaithasan • 6 minutes
Mar 21, 2020

Most hello world kind of repositories have a top level src/components melting pot which gets stuffed with all types of components. While this works for demonstration purposes and tutorials you will quickly find yourself inundated with a plethora of subdirectories underneath your catch-all melting pot when you try to replicate the requirements and complexities of a real application. Not just will it become tiresome to quickly navigate to a particular component in your editor, it will also hide insightful parent-child relationships that may exist between components. The biggest threat to a poorly designed directory structure is an increased cost in maintainability and a reduction in agile product development speed. Let us inspect how atomic design principles made their way into React to help us stay organized.

Disclaimer: Atomic design principles are not an universally agreed upon set of rules that must be adhered to. Rather they provide an impulse on how to tame a complex construct into consumable, reusable and isolated chunks. It is up to the developer on how he or she interprets these guidelines. This article is about how I currently go about structuring large React applications.

The terminology and concepts we are using to construct user interfaces in React are borrowed from the field of chemistry - the science that deals with the composition, structure and properties of matter. Anything which has mass and occupies space is called matter. The basic constituents of matter are atoms and molecules. Finally, organisms - a special type of matter - may be defined as an assembly of atoms and molecules that exhibits the properties of life. An individual animal, plant, or human being are examples of an organism. The analogy provides a mental model in how a complex user interface (UI) can be decomposed into standalone, reusable, and maintainable building blocks.

First up, let us create the corresponding src/components/atoms, src/components/molecules, src/components/organisms, src/components/templates and src/pages subdirectories.

Atoms

Examples of atom components
Fig1: Examples of atom components

Atoms represent the most basic and fundamental building blocks in this interface design methodology. In a website, some of the most basic building blocks are buttons, icons, spinners, headings, and images. These components are characterized by the fact that they...

  1. can be reused across the entire site
  2. can bond together into groups and form a molecule
  3. are independent from other components

Since atoms form the foundation of every user interface, I recommend to properly unit test every single feature they provide. react-testing-library is a good tool to inspect whether or not certain classes and styles are added and removed in accordance with a given user interaction. It can also be used to inspect whether or not a particular element has been added or removed in response to an event fired by the user. While react-testing-library is capable of much more, the testing methods I just described cover 90% of what is worth testing in the first place.

If you yearn for additional assurance that your component behaves and appears as expected, I can recommend visual regression testing. However, it requires that you have a component gallery in place capable of rendering nothing but the atom component at a deterministic url. storybook is my favourite contestant in that space. You can have a look at our corporate component gallery which we have dubbed sushi. Using cypress you could navigate to the atom url of interest, take a snapshot and visually compare it to the previous snapshot whenever the underlying component code base has been altered. I consider visual regression testing as icing on the cake and really only opt in if a) the budget is available and b) the component possesses a myriad of states that cannot be easily described using class names. In large product teams, visual regression testing provides an excellent mechanism to streamline expectations put out by designers and product owners with code written by developers - a beautiful symbiosis.

Molecules

A bond of atoms that forms a logical, easily referable, and reusable piece of the user interface is what we call molecule. A Card component is a typical example of a molecule. It is basically a composition of a title, a description paragraph, an image, and a button atom component that can be commonly referred to by the name of Card.

Likewise, a random combination of a progress bar, a tag, a checkbox, and rating component does not yield a logical easily referable name and thus are not to be considered a molecule.

Taking a closer look at Fig1 reveals that the "Submit" button component does not formally fulfill all the rules that we have defined for an atom. In this case, we are not simply displaying a plain old button. Instead, we are dealing with a special type of button, i.e. one that is rendered alongside a "pen" icon. So shouldn't the "button with icon" component actually be classified as a molecule? The answer is no.

The classification of components into one of these buckets is more art than it is science.

Following the atom rules - or guidelines rather - too rigorously will have an undesirable classification effect leaving the atoms bucket almost empty. Recall what it means to be named an atom. Such a component will experience massive test coverage, code reviews, design iterations, etc. because it is fancied as a basic or fundamental building block. If you make a coding or design mistake on one of these, the damage will be traversed into all molecules making use of it. The button is still a basic piece of UI which will be useful in a number of molecules. Just because there is this one state of the button, that makes use of the icon component, does not mean the button is no longer worthy to be an atom. You have two options to deal with such a case: either you create a standalone <ButtonWithIcon /> molecule and risk to clutter your component repository or you accept that the <Button icon={() => <Icon />} /> is effectively still an atom although it formally does not fulfill the independence imperative.

The <Accordion /> component is the epitome of a molecule. It makes use of title, button, image, paragraph, and icon atom components as well as adds additional UI and logic on top of it. A developer, who is assigned to build such a component won't have to deal with the intricacies of the underlying lower level atoms, but only with the specific logic to conditionally open and close containers. Unfortunately, the above arguments could be applied here as well to justify that the <Accordion /> too is a basic or fundamental piece of UI, and that it therefore must be an atom. Such a trivial classification approach would leave your molecules bucket empty.

Atoms deal with a small but standalone piece of UI and a very limited scope of logic. Molecules are nothing more than a smart grouping of atoms alongside additional logic possessing some of the key traits of atoms. They too, must be reusable and be easily referable by a common name.

Yet the words "small", "limited", and "common" are subjective in nature. Atomic design cannot be applied with a hard set of rules. It requires experience and tactfulness to make the right classification and naming calls on a component.

Organisms

Organisms are also a bond of components. However, unlike molecules they are not necessarily reusable and most likely are not easily referable by a common name either. At the outset we have described that organisms exhibit the properties of life. In a programming context, organisms are a bond of atoms and/or molecules that fulfill specific domain requirements or that solve a particular user story. A <ContactForm /> or a <Pricing /> component are good examples of an organism. While the former expresses the user story in a succinct and elegant way, the latter does not provide concrete indications on what it is going to display.

Under the src/components/organisms directory let us create one subdirectory per page. Let us assume our website has a contact, an about, a services and a blog page. That would result in src/components/organisms/contact, src/components/organisms/about, src/components/organisms/services, and src/components/organisms/blog subdirectories.

In these subdirectories we eventually place the organisms.