DEV Community

loading...

Good or bad: Styled components theme helper

jsaele profile image Joakim Sælemyr ・2 min read

So I've been using styled components with theming lately. And I'm wondering a bit about my approach. It might have poor performance, and it might use a theme for more than what it's intended for.

Using the theme obj for more than just colors

On projects I've done in the past where I've used css modules with scss, I've found it convenient to have lots of common variables to ensure consistency in sizes (margin, padding, font etc) and colors throughout a codebase. Also to be able to easily change them/and or changing themes etc.

So a theme object might look something like this (example values):

const theme = {
  width: {
    content: '60vw'
  },
  spacing: {
    sm: '0.25rem',
    md: '0.5rem',
    lg: '0.75rem',
    xl: '1rem'
  },
  color: {
    background: {
      primary: 'black',
      secondary: 'white',
      someOtherColor: 'pink'
    },
    primary: 'white',
    secondary: 'black'
  },
  font: {
    heading: {
      sm: '1.4rem',
    },
    text: {
      sm: '0.75rem'
    }
...

The attempt is to have all common properties in the same place, and be able to structure it so it's not horrible to get an overview of.

So using the values from the theme above in a component would look something like this:

const MyComponent = styled.p`
  color: ${({ theme }) => theme.color.primary};
  background: ${({ theme }) => theme.color.background.primary};
`;

This gets pretty tedious to write all the time, and I also feels it adds to much noise, reducing readability.

Q: Is it fine using theme for sizes etc in addition to colors?

Using a get helper func instead

Since I wanted a visually cleaner and lower effort way of accessing different nested properties in my theme obj, I wrote a function I just called get. Comparing the component above, it looks like this:

const MyComponent = styled.p`
  color: ${get('colorPrimary')};
  background: ${get('colorBackgroundPrimary')};
`;

get expects a theme property to exist on the component props object, and also a camelCased identifier which is used to look for the value. So, in my opinion it's less noise in the styling, and also easier to type.

Q: Since this is a function, instead of just accessing props on an object, it has to perform some operation to know where to lookup the value. So it'll definitively have a performance hit. But will using a function for purposes like this have an actual noticeable/critical performance hit regarding smooth rendering etc?

Guess it probably depends on the implementation and device..

Anyway I'm liking the approach, it's straight forward to use and readability is not suffering too much.What do you think?

If anyone's interested, I can share the func.

Discussion

pic
Editor guide
Collapse
andyford profile image
Andy Ford

Good! =) But maybe already built? ... github.com/styled-system/styled-sy...

The only drawback to theme-get I've found is it doesn't offer any type safety if using with TypeScript.

What I have settled on is to use the styled-components ThemeProvider higher up in the app so that all components get a 'theme' prop, then in individual components:

import styled, { css } from 'styled-components';

const MyComponent = styled.p`
    ${({ theme }) => css`
        color: ${theme.colorPrimary)};
        background: ${theme.colorBackgroundPrimary};
`}
`;

... wrapping the whole declaration block in the css function is still more boilerplate than I'd like, but now you don't have the verbosity in every reference to 'theme'

there's a lot of discussion on this topic here: github.com/styled-components/style...