Tidy up styled-components with composition

Simon Jentsch
Oct 17, 2019 · 3 min read

How to write more readable styled-components with higher order functions.

Higher Order Functions help you to make your code more descriptive

If you ever used styled-components you most probably have made heavy usage of function calls to access your props like this:

const StyledLink = styled.a`
color: ${props => props.color};
`;

Using props to define styling behaviour is awesome! It feels way more natural and descriptive than combining multiple CSS classes with each other. Also in combination with TypeScript you can add discoverability of styling options to your components. But there is a case, where using functions to interpolate your CSS can get really messy und unreadable: theming.

Take the theme object of material-ui for example. Accessing values of a more complex theme object can result in this:

const MyBox = styled.div`
background-color: ${props => props.theme.palette.primaryColor.main};
color: ${props => props.theme.palette.primaryColor.contrastText};
padding: ${props => props.theme.spacing(2)}
`;

Having a hard time reading this? Me too! Also when it comes to scanning code this is a blocker as you need to stop every time to figure out what value is actually accessed here.


There are multiple problems here:

  • When reading functions you expect them to be potentially more complex than just accessing values.
  • Accessing deeply nested objects results in long lines and walls of text.
  • Seeing the props keyword makes you think of using component props rather than accessing the theme context.
  • Arrow functions can add a lot of visual clutter.

You can solve quite a lot of these problems with higher order functions.

Instead of writing the interpolation functions every time, we are writing abstracted functions for commonly done things like accessing color or spacing. And because of the repetitive access of theme values, this even saves you quite some time writing your styles.


Let’s see how the above example could look with using higher order functions.

const MyBox = styled.div`
background-color: ${color('primary')};
color: ${color('primary', 'contrastText')};
padding: ${spacing(2)}
`;

Way easier to to read and scan, right?

Let’s implement the color function:

const color = (variant, type = 'main') => 
props => props.theme.palette[variant][type];

Additionally you could create shortcut functions like this:

const primaryColor = type => color('primary', type);
const secondaryColor = type => color('secondary', type);
const contrastTextColor = (variant) => color(variant, 'contrastText');

But you don’t need to stop there, you can even build something like a styled-system inside more complexly styled components. This is extremely handy when you want to combine more than 4 or 5 different styling parameters (that otherwise would make your JSX less readable when using styled-system).

const MyBox = styled.div`
${mx(2)};
${mt(1)};
${mb(3)};
${shadow(2)};
display: flex;
justify-content: space-between;
`;

The benefit of using something like this, is that you can combine more complex styling together with design system values like spacing, shadows or colours. Writing this in a system like styled-system or @material-ui/system adds a lot of noise in your actual JSX.

<Box mx={2} mt={1} mb={3} shadow={2} display="flex" justifyContent="space-between">
// ...
</Box>

Also it cuts down time writing common css property names hundreds of times.

The margin functions implemented in with the material-ui theme could something look like this:

Conclusion

Higher order functions give you many creative possibilities to write helper functions, that increase readability and scan-ability, reduce coding time, and combined with descriptive naming also help others understand your styled components better.

What is your experience on higher order functions in combination with styled-components? Do you have questions? Or do you have some more handy use cases for this? Share your thoughts with me, I’m interested!

P.S.: We are currently building a service for visual component review in GitHub Pull Requests. This way you can quickly show new and changed components to designers, so they can give feedback even before your Pull Request is merged. If you are interested, visit our website www.bojagi.io. You can add yourself to the beta waiting list and get early hands on.

Simon Jentsch

Written by

🇪🇺 Frontend Engineer @deliveryherocom with passion for Design and UX

bojagi

bojagi

Bojagi makes your designers visually review components before your code is being merged.

More From Medium

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade