How to Build a Composable Stack Component
Photo by Sean Stratton on Unsplash One of the simplest and yet most common layout patterns found on the web is putting one element on top of another element with consistent space between them. From form labels, to paragraphs of text, to social media feeds. They all need to stack one thing on top of another with uniform space between them. Let's say we are building the following component: As you will notice, we have a few parts to this widget. There is a title section and a form section made up of two input groups and a button. The one thing that they all have in common is that they follow the same pattern. They all stack vertically with space between them. Here is the same mockup with the different space sizes pointed out. What we need is a way to enforce that all the items will stack in the block direction. We also need a way to provide consistent space between the elements without creating space around the stacked elements themselves. That way, the stack can remain composable in any other environment. (I will be using Styled Components in these examples, but everything can easily be translated to any flavor of CSS, including vanilla CSS.) Let's start with some basic markup. And this is what that looks like: The first problem we want to solve is to get the labels to stack on top of our inputs. This problem is easily solved with a single line of CSS: We create a new Stack component and set the display property to grid , thereby implicitly setting a single column track for items inside the Stack. Each item that is placed in that column creates an implicit row. We can then use this new Stack a component like this: Our component has already taken a major step in the right direction: The first thing you will notice is that not only are the labels stacked on top of each other, they also now take up the entire width of the column. All we need to do now is set the space between the items. We set the space between items using the gap property, which takes any valid sizing unit, such as px , % , or rem . To make it configurable via props, we will take advantage of the styled-components string interpolation and derive the value from props, like this: (If the syntax above is unfamiliar to you, it uses tagged template literals that are part of ES2015. You can find out more about tagged template literals over at MDN ) When we use styled.div we are calling a tagged template literal that will let us pass in a function inside the ${} that gives us access to the component props . We can then customize what the CSS looks like depending on the value of the props . The above code takes the value of the gutter prop and uses that as the value of the gap property. We could have used any name for the prop , such as space or even gap . I chose to use gutter as it is a common term when talking about page layouts. If no value is passed to gutter , it will fall back to 1rem . This means we can rewrite our Subscribe component like this: And now our component looks like the final version: We have a component that will universally stack all of its children and will separate them via a configurable value that passed into the gutter prop. We could stop here, but I would recommend one more tweak to the gutter prop. Right now, we are allowing any value passed into the gutter. It is considered best practice to adopt a spacing scheme when you are laying out items on the web for consistency. Choosing a good spacing scheme is beyond the scope of this post. For simplicity, let's use a spacing scheme based on t-shirt sizes and looks something like this: Now, all we need to do is adjust our Stack component slightly, like this: and update our Subscribe component like this: The Stack component is a straightforward yet powerful tool that allows you to compose vertical stacks of elements on your page.