Over the last few years, CSS-in-JS solutions have become prevalent across the front-end landscape with many offerings such as styled-components, and emotion that provides a way to colocate your components and style definitions.
The power of these libraries lies in the ability to use JavaScript features to enhance reusability & easily create a design system that goes beyond how things appear visually.
For this post, we're going to focus on styled-components
specifically and how it has been changing the way people style their React components.
While there are many advantages to using a "CSS-in-JS" solution, naturally, it's quite a polarizing topic amongst front-end developers. I'm going to lay out some of the essential pros and cons of styled-components
vs. more "traditional" CSS stylesheets that affect the way I code in React on a day-to-day basis.
Styled Components
Pros
No Globally Scoped Selectors
One of the most significant benefits of using styled-components
, in my opinion, is no longer having to worry about your selector names existing in a global scope with cascading overrides.
Consistency
UI libraries are very prevalent in front-end development. They can be great for getting off of the ground quickly, but typically require you to override the CSS selectors written by the author to customize the styles to match your designs.
When using a CSS framework, this can create an unnecessary learning curve, or at the least, you'll find yourself continually tabbing back and forth to find the correct name of the selector you're trying to override in an ocean of CSS.
styled-components
make it easy for you to publish a component to NPM and ensure that it is not only super customizable for the user through props and/or extending via styled(Component)
but that it always looks & behaves as it did locally due to zero chance of clashing selectors.
Sass Syntax Out-Of-The-Box
In your styled-components
definitions, you can use SASS syntax out-of-the-box without having to set up any preprocessors and extra build tools.
Inside your style definitions, you can use the &
character to target the current component, and from there, you can target the parent element, children, or sibling elements as well as adding pseudo-selectors.
See the Gist below for some quick examples of you can leverage this syntax to change the styles of a component based on its surrounding elements:
const List = styled.div` // Target immediate children of List that have siblings & > * + * { margin-top: 16px; } // Target any List components that are the child of an <article> article & { color: white; } // Target any adjacent Lists & + & { margin-top: 16px; } // Target the List when hovered &:hover { background-color: red; } `;
Theming
Using Reacts Context API, styled-components
provides a ThemeContext
that can you can pass a theme object to, making it accessible in any of your components, and by default can be interpolated into your styled
definitions.
I often use the theme to store colors, gutter sizes, and utility functions that can manipulate colors, generate shadows & bezier curves, and much more. The world is your oyster here as far as creating a reusable design system and theming makes it really easy to pull all the raw values & colors from a Sketch file and keep every component on the same page.
Tip: I find that for colors, it can be beneficial to use more generic names like
text
,primary
&accent
so that the names are not specific to the color value itself. By being more generic, it enables you to create multiple themes (such as a "Dark Mode") with the same key names so that you can switch them out on the fly without having to change any of your style definitions further down the component tree.
Here is a quick Gist with an example of how you can access the theme in styled-components
.
import React from 'react'; import styled from 'styled-components'; // By using string interpolation to wrap a function in // your style definition, you can access the props of the // component & the theme. const Button = styled.button` color: ${({ theme }) => theme.color.blue}; `; export default () => { return ( <Button /> ); };
Dynamic Styling
In the same way that you can access your theme, as shown above, you can also access any props passed to the component, opening up powerful opportunities to create reusable, customizable components.
One example of this would be a styled button. Buttons may have multiple sizes, color variations, and other stylistic differences depending on where they appear on a page. However, the markup and any logic would likely be identical between the variations.
Rather than creating multiple class definitions in your undoubtedly behemoth CSS file, you can leverage props to dynamically change the styles in a way that feels natural to anyone comfortable with React.
One way that I use this feature personally is to create a reusable typography component that I can use throughout my app, without ever having to create multiple style definitions.
Tip: See the Gist below for an example of also utilizing the
as
prop. This prop is a relatively new feature ofstyled-components
that takes the above concept one step further. We also pass a color name from our props to our theme so we can use our color scheme without extending the style definition.
import React from 'react'; import styled from 'styled-components'; // By default, the Text component will render // to the dom as a <p> tag. const Text = styled.p` color: ${({ color, theme }) => theme.color[color]}; font-size: ${({ size }) => size}px; font-weight: ${({ weight }) => weight}; line-height: ${({ lineHeight, size }) => lineHeight || size + 8}px; `; Text.defaultProps = { color: 'text', size: 16, weight: '500', }; // Below we pass "h1" to render the Text as a // heading, whilst maintaining the style definition. // Under the hood, styled-components just passes // the generated className prop to the `as` element. // This means you can also pass in a react component // like so <Text as={MyOtherTextComponent} />. export default () => { return ( <div> <Text color="primary" as="h1" size={24}>Title</Text> <Text>Paragraph</Text> </div> ); };
Cons
Learning Curve
Most, if not all, front-end developers know CSS in some capacity, and styled-components
requires you to think a little differently to traditional CSS. Fortunately, the learning curve is relatively short once you are used to styles & components living together.
Additionally, all of styled-components
more "advanced" features are optional, and you can use plain old CSS with no issues at all - although this is often an unnecessary hurdle for a team of developers that aren't all on the same page with CSS-in-JS.
Integration With Legacy CSS Can Be Painful
.
Whether you are using something like MaterialUI, or even your existing stylesheets, integrating styled-components
alongside them can be confusing to locate & debug styles. CSS files and styled definitions are often nowhere near each other in the codebase (or even largely inaccessible in the case of CSS Frameworks) and give you no means of seeing which CSS definitions relate to which styled-components
and how the styles cascade as far as overrides.
Potentially a "Fad"
In comparison to CSS itself, CSS-in-JS is an extremely young concept and naturally is liable to disappear as quickly as it appeared.
Becoming obsolete or going "out of fashion" happens all the time with tech, sometimes sticking around for a while and other times for a relatively short burst of hype.
It can be frustrating for developers - especially if it means refactoring or even totally rewriting your styles down the line when "the next big library" shows up. This fact also accentuates itself - as the old saying goes, "If it ain't broke, don't fix it."
I think it fair to say that CSS and more established frameworks & preprocessors like SASS are far from broken.
Performance
In larger applications & websites, performance and caching of assets is instrumental in maintaining a solid user experience, especially as tech becomes more readily available in areas with slower connections.
styled-components
parses all of the style definitions into plain CSS at build time and drops everything them all into style
tags in the head of your index.html file.
The issue here is that even if you are statically generating your site with something like Gatsby & React, not only is the size of HTML file increasing, but there is no way to chunk the output CSS either.
Moreover, the class names themselves are dynamically generated, too, essentially breaking caching as things can change between builds/renders.
Tip: One way to ease this issue slightly is utilizing code-splitting in your app (check out react-loadable or React Code Splitting ) - because
styled-components
happens in JS land, code-splitting ensures only the styles & javascript that are necessary for the current page are sent down to the browser, and the rest can be lazily fetched as the user navigates around.
Colocating Can Bloat Your Components
When styling the DOM in any way, not everything works as a reusable component that can apply to multiple elements.
Often styling rules can be specific to one element that is never reused, and for styled-components
specifically, there may be a component that is imported and then extended multiple times in different files, creating a lot more code than necessary.
Of course, it makes no sense to abstract the code into its own file in your components
folder, so the most obvious place for it is in the same source file as its parent component.
This is a common pattern I see with styled-components,
and oftentimes, the first 100/200 lines of a components source code is a whole bunch of styled component definitions.
CSS Stylesheets
Pros
Unopinionated and Universal
CSS is universal and has no opinion on how you render your UI making it great for teams that have legacy CSS and are migrating over to a new framework or rebuilding their website or product.
For example, you can easily use the same stylesheet in a React project & a Vue project with zero friction, making it especially useful if a team shares styles between a website & a web application that are written in different languages. Even if you use a preprocessor, it ultimately compiles to CSS and still works universally.
Caching & Performance
Standard CSS files are easy for the browser to optimize for, caching the files locally for repeat visits, and ultimately giving performance wins. Although it could potentially mean more network requests, CSS files tend to be small as far as actual file size.
Quickly Iterate A New Design
You can very easily rip out the entire stylesheet and create a new one to refresh the look and feel of your app without digging through potentially hundreds of components.
You can also split your stylesheets into specific files to create some modularity. For example, creating a separate file for your grid system and design elements so you can retain your layout but completely alter the look and feel.
However, in favor of styled-components
, if done correctly, you can achieve the same result with a much easier, cleaner developer experience through theming.
Ease of Use
Another reason that a team may choose to go with CSS over styled-components
is the fact that it is a lot easier for a developer with styled-components
experience to understand vanilla CSS. On the other hand, for a developer that knows CSS well but has never used styled-components
, it can be confusing and create unnecessary ramp-up time while they get past the learning curve and figure out how to navigate a projects source code.
Frameworks
For a new developer, CSS frameworks are a great way to understand how things work while providing you with building blocks to get your idea off the ground.
With styled-components
, someone with little to no JavaScript experience would have a hard time understanding the code.
Even a seasoned JS developer with no React or styled-components
experience would likely need to stop and think for a moment once they run into features like interpolating props for conditional styling.
Cons
Readability
Anyone who has ever built a full-scale app or website with using traditional CSS knows that very quickly, the stylesheets can become incomprehensibly long and difficult to navigate.
If you work between multiple projects of this size of a few months, switching back and forth and trying to make sense of these files can be tedious, to say the least.
The only plausible benefit of this would be the very blatant rules for overrides which is something that styled-components
totally solves - and once people start to use more granular selectors like div > p
and then forcing their styles to work with !important.
it can become almost impossible to maintain.
Legacy CSS Can Live On For Years
Often these monstrous stylesheet files become so complex and long-winded that cleaning up old, outdated, or unused styles is like finding a needle in a haystack, and although it only makes things worse, developers can just create a new style definition and override the styles instead.
Global Scope & Specificity
The global scope in CSS means that every unique style definition must have a unique selector name. Also, due to the cascading nature of CSS files, you can easily become confused by why something doesn't appear to be working correctly, or styles begin to change each other.
This often introduces the need for strict naming conventions and comments to ensure that everything is accurate and works in a reliable way when making additions, removing old code, and finding your way around the files.
No True Dynamic Styling
If we want to conditionally change the styling of an element with plain CSS, even when using a framework such as React, it can take an unnecessary amount of code to pull off.
We need to target a DOM element that has the class we want to style dynamically, create another class for the style changes, usually add some event listener and then conditionally add or remove the new class from the element.
In all fairness, React makes some of this easier with event callbacks, state, props, and component re-rendering, but in comparison to the alternative with styled-components
, it can save you a considerable amount of time and code.
Maintaining Consistency
Having a theme in styled-components
goes a very long way in making sure that your styles are consistent & easily modifiable across your entire.
While vanilla CSS has support for variables, allowing users to define reusable values within their stylesheets similar to them, the syntax is annoying and only bloats your CSS file with more and more code you need to search around in to remember the correct variable names. Again, this is usually solved with a disciplined naming convention and structure that the whole team would need to be at least aware of.
Wrapping Up
Overall, as an Engineer who writes React every single day I feel that styled-components
is a must-have dependency for almost every project and can be a very powerful tool once you start to switch the way you think about styling from purely making things match a design to creating reusable, composable and adaptable style definitions that not only benefit from the modularity and ease that React is so loved for, but that come together to form a full design system that can adapt alongside the rest of your app from one source of truth.
Vanilla CSS will likely never disappear, certainly not any time soon. However, if you spend any amount of time creating UI from a design file using React to build your views - and you somehow haven't tried styled-components
yet, I really cannot recommend it enough.
I'd love to hear your opinions on Styled Components, have you used them? Are you a die-hard Stylesheet fan? Let's talk about it!
Disclaimer: Stream provides Feed and Chat APIs that power digital experiences for over 500,000,000 end-users, and we use a mix of styled-components and stylesheets within many of our apps, SDKs, and component libraries. If you're interested in checking them out, I recommend taking a look at our Stream Chat React Components, and you can learn more about our awesome messaging platform here.
Happy Styling ✌️