Stop using margin, use Spacer component instead!
A better way of handling spacing between components in React

When coding for UI layout, we need something to represent the spacing between elements, and yes, we have been using margin
for a long time.
However, when using a component-based framework such as React
, we may need to rethink if margin
is still the best choice.
Here are my thoughts:
The problem of margin
First of all, I would say it is not good to use margin
on the component’s top-level, because it breaks the isolation of components.
// A React component with margin set on top-level
const MyComponent = () => {
return <div style="margin: 1rem">...</div>;
};
margin
here is not just affecting the component itself, but also affecting other components (e.g., it is pushing off another component / element sitting next to it)- If we want to place this component in a different context or layout,
margin
would get in the way of resuse
Then, what about using margin
inside a component (not on the top-level)? It seems to be fine at the first glance, but when you extracting a part of the existing component to assemble a new component for whatever reason (e.g., reuse / isolate / simplify / performance), it will bring us back to the problem of having margin
on the top-level again :(
Solution: Spacer component
Instead of regarding empty spaces between components or elements as margin, let’s imagine that they are actually components — components that are dedicated to represent the empty space, and we can call them Spacer
.
It is not something completely new to the UI programming, but something already widely used in many UI frameworks (especially on mobile):
Why not having one for the Web / React? Here we come:

Once we abstract the spacing as a decidated component, we won’t have the problem of breaking isolation by using margin
! All components don’t need to care about what is the required spacing around them — the parent component will take care of that via using Spacer
components, which makes every component really self-contained and reusable.
A simple exaple would be using Spacer
to represent the vertical gaps between parts of an article:
<Article>
<Header />
<Spacer y={2} />
<Section1 />
<Spacer y={1} />
<Section2 />
<Spacer y={2} />
<Footer />
</Article>
A possible implementation of the Spacer
component (based on the styled system of Material-UI) could be:
That is, you could either specify the width
, height
or flexBasis
as the value that would be multiplied by theme.spacing
(same as other spacing props of Box
)
Going further: layout component using Spacer internally
There is a lot of chances that we may want to make spacing between components / elements even, and it is not elegant to drop in many Spacer
components with the same props
. In this case, a Container component can definitely help.
For example, we may have a Flexbox
component as the container, which accepts a spacing prop to config the Spacer
components inserted between its children items (some other articles may reword it as Stack
components).
<Article>
<Flexbox flexDirection="column" spacing={1}>
<Header />
<MainContent1 />
<MainContent2 />
<Footer />
</Flexbox>
</Article>
A possible implementation of the Flexbox
component (based on the styled system of Material-UI) could be:
Bonus: dynamic spacing in flexbox
Apart from making the component self-contained and reusable, Spacer
component brings us one more benefit that we are not able to get from margin
easily— dynamic spacing in flexbox.
This is the natural outcome of turning spacing into components — we can now treat them as flexbox items which can grow
and shrink
!
For exampe, imagine we have a layout below (the number in picture indicates the width of the box):

Given the conditions:
- the width of two left items are fixed
- the right item and spacing can be shrinked
- we want to make the spacing shrink 2x faster than the right item while resizing the container.
It would be straightforward to get something like this:
<Flexbox>
<Box flexBasis={100} flexShrink={0} />
<Box flexBasis={100} flexShrink={0} />
<Spacer flexBasis={200} flexShrink={2} />
<Box flexBasis={200} flexShrink={1} />
</Flexbox>
Based on that, when the right Box
shrink to 150, the Spacer
would shrink to 100.

Conclusion
Let’s stop using margin
!
It breaks the isolation of components and makes them harder to be reused in different contexts.
Spacer
component and layout components based on it (e.g., Flexbox
/ Stack
) will solve these issues, and they can bring extra benefits when you want more flexibility on spacing!
Updates after the original post
- Flexbox now has a
gap
property to define the spacing between its children elements, but it is not supported in all major browsers yet (no IE, no Safari < 14.1). Once it is widely adopted by mordern browsers and users, usinggap
withflexbox
should be the recommended solution in most use cases.
(Tip: if you use theFlexbox
component approach, then it should be easy to switch from<Spacer/>
togap
in the future as it is just an internal implementation change, and<Spacer />
will still be useful when you have extra and/or special needs to represent the spacing between components) - Re: Performance, the essential way to avoid performance issues with long list of items is virtualization (or “render in window”) whether you use empty div elements or not — if you don’t virtualize, the long list hurts the performance whatsoever; if you do virtualize, then why bother with a few more DOM elements in a short list?
Related articles
- Margin considered harmful (by Max Stoiber, co-author of styled-component)
- Don’t Fear Empty Divs (by Cory Etzkorn)
- Spacing in CSS (by Ahmad Shadeed)
- Layout-isolated components (by Emil Sjölander)
- Handling spacing in a UI component library (by Chris Pearce)