Styled-components is a styling library for React. As the name suggests, styling is applied at component level. Styled-components is a so-called CSS-in-JS library, meaning that styles are generated from Javascript (or JSX) files.

I’ve been using the library for a few months in two quite different React projects. Now it’s time to share some of my experiences with it.

Initial adoption

Several months ago I was still very sceptical of CSS-in-JSS. I remember an old React Native project and how tedious it felt to write pseudo-CSS in JS objects. I also immediately thought of the limitations of inline CSS and the verbose syntax choices of some styling libraries.

I first encountered styled-components in React Amsterdam. A creator of the library, Max Stoiber had a great talk on the subject that left me impressed for several reasons. First of all, the markup was real CSS – No JSON, no camelCase. Second, the source was transformed to stylesheets, not inline styles. This means that support for features like pseudo selectors, media queries and nesting should be no problem.

Finally, I really liked the simplicity and robustness of the API. Using template literals to pass JS expressions to stylesheets is a nice and transparent way to support dynamic styling.

I started integrating the library into an existing early-stage React project the week after the conference. This was the start of a new era in frontend development for me.

Development experience

Styling components with styled-components is straightforward and fast. Typically, one declares a new constant variable and assigns a styled element function to it.

const Title = styled.h1`
  font-size: 1.2em;
  color: #333;
  text-transform: uppercase;
`;

function PageTitle() {
  return <Title>Hello world!</Title>;
}

Most of the time, the syntax is just CSS. Indentation can be used normally. One has to be careful with things like semicolons after rules, as the new CSS processor that comes with version 2 is really strict on syntax. There are no error messages for malformed CSS which sometimes leads to hard-to-find mistakes.

Debugging broken CSS manually can be tedious. This problem could be solved with advanced tooling like linting with CSS rules inside JS files. There are already CSS syntax highlighting editor plugins (at least for Atom and VS Code) for styled-components. Linting support should be the next iteration in tooling.

For dynamic CSS rules, styled-components uses template literals. Styled functions are invoked with the new backtick syntax. Without going to into the details, this enables the function to receive all interpolations inside the string literal as extra parameters. This feature is crucial to how styled-components works.

String interpolation makes using variables in CSS rules slightly more verbose than in preprocesssor languages like SASS. In SASS, one would write

padding: $spacing-unit / 2;

which in styled-components (utilizing the theming feature) would be

padding: ${p => p.theme.spacingUnit / 2};

If you use a large amount of theme variables, this will add some verbosity to writing styles. I don’t think it really reduces readability (especially if using a syntax highlighting plugin) but it does make writing the expressions a bit more tedious.

It is possible to write SASS-style mixins using plain JS functions. Again, the syntax in the template is a bit heavier than with preprocessors. Runtime JS however provides significantly more power and versatility than compiling to CSS on build time.

Overall, the development experience of writing styles with styled-components is really good. The biggest benefits are strong encapsulation and reducing the amount of context switching. Not having to switch between CSS and JS files to add or check classnames is really a bigger benefit than one would imagine. The best class naming convention is no classnames (or rather auto-generated ones).

Server-side rendering

The reality is that most React frontends need to be rendered on server for reasons like SEO, perceived performance and accessibility. Both of the projects I’ve been working with utilize SSR.

When I first started using styled-components, there was no support for SSR. The feature was going to land in version 2 which at the time had no announced release date. It was possible to hack together SSR by using the library’s internal APIs.

I decided to wait for the major version update as I was in no hurry with the projects. During development there was an annoying flash of unstyled content on initial page load as the HTML was rendered on server and styles added in client JS but I counted on the upcoming SSR support.

Styled-components 2.0.0 was released less than a month ago. I was pretty excited about this – I bumped the version and enabled SSR for one project the same day. This was an easy operation that really improved the perceived performance of the site. The version update also reduced the size of the library significantly by swapping CSS processors. This also made the processing more strict on CSS syntax which I think is a good thing in the long run.

With my other project I encountered some problems which lead to me having to delay enabling SSR. The reason for this, Typescript, is discussed further down.

Typing support

More and more Javascript projects are adopting type systems to leverage better static analysis and to ensure code correctness. In React scene, Flow is the de-facto solution as it is backed by Facebook and integrates into React nicely.

One of my recent projects is written in Typescript. This sometimes leads to difficulties with React and related libraries. Most of the bigger projects have good and up-to-date TS definitions but smaller and newer libraries often don’t.

Styled-components had TS definitions available when I started using it. This was a big plus to me. With the release of version 2.0.0, it took a bit more time for the new API functionality to receive Typescript support, which lead to me having to delay upgrading on the Typescript project. The typings landed in v2.0.1 and I also got to contribute some type definitions to it!

Defining types for components using styled is a bit tedious. There is no definite way to pass props to the created component other than defining them in the template interpolations.

type Props = {
  active: boolean
  title: string
};

const NavItem = styled.li`
  color: ${(p: { active: boolean }) => p.active ? 'goldenrod' : 'darkgray'};
`;

function NavigationItem({ active, title }: Props) {
  return (
    <NavItem active={active}>{title}</NavItem>
  );
}

This is the basic case but things get a bit more complicated in the following situations

  • using the theme functionality at the same time
  • the element also takes props not related to styles (event handler, href, etc.)
  • the prop is used inside a template mixin

So far I’ve managed to handle these cases but it often feels more like hacking things together than writing type-safe code. The problem isn’t the library, this is more related to how React components handle prop typing and to Typescript in general.

I haven’t experimented with using Flow to type styled-components. It would be interesting to hear whether the situation is better with Flow. I would imagine that some of these issues do carry over to Flow also.

Typing styling code sometimes feels like unnecessary work. It takes quite a bit of time compared to the benefits one gets. The typing coverage doesn’t reach the actual CSS part making the set of errors possible to catch quite small. Again, I’d like to emphasize that this isn’t the styling library’s fault. Strong typing, React component props and CSS-in-JS just don’t play along too well.

Conclusion

Overall, I’ve been really happy working with styled-components. It was easy to get into the library and I feel I could use it productively in less than a few hours after running the first npm install. The concepts it introduces are powerful and there is just enough features – nothing extra.

CSS isn’t the nicest styling system and there have been many attempts to make it better with tooling like pre- and postprocessors, libraries and alternative syntaxes. Styled-components hits the sweetspot for someone who is familiar with CSS but feels limited by some of it’s shortcomings.

I will definitely choose styled-components for my next React project too. I’m also looking forward to trying it out in a React Native project some day.




While writing this text, I encountered a feature in styled-components I was not aware of. It is apparently possible to refer to other components in a rule. This is pretty cool and will definitely improve style encapsulation in my future components!

https://www.styled-components.com/docs/faqs#referring-to-other-components