Editor’s note: This post was last updated on 29 July 2021 for accuracy and clarity. For more up-to-date information on CSS-in-JS, you can also check out this article on CSS-in-JS libraries.
In addition to traditional CSS, inline styles and CSS-in-JS can also be used to style React applications.
Even though inline styles enable you to pass a JavaScript object to the style attribute (as seen in the code snippet below), it doesn’t support all CSS features:
import React from "react"; function App() { const myStyle = { fontSize: 24, lineHeight: "1.3em", fontWeight: "bold" }; return ( <div> <p style={myStyle}>Hello world</p> </div> ); } export default App;
On the other hand, CSS-in-JS libraries like Aphrodite, styled-components, JSS, Emotion, Radium, etc. give developers the ability to not only style components with JavaScript but also tackle some CSS limitations, such as the lack of dynamic functionality, scoping, and portability when using them:
// Here's an implementation of the inline style code snippet above using Aphrodite import React from "react"; import { StyleSheet, css } from "aphrodite"; function App() { const styles = StyleSheet.create({ myStyle: { fontSize: 24, lineHeight: "1.3em", fontWeight: "bold" } }); return ( <div> <span className={css(styles.myStyle)}>Hello World!</span> </div> ); } export default App;
In this article, I will highlight five things you didn’t know you could do in CSS-in-JS using the CSS-in-JS libraries mentioned above as a case study. 👇
Libraries like styled-components and Emotion allow you to use tagged template literals to create React components from styles:
// Create a component that renders a <p> element with blue text import React from "react"; import styled from "styled-components"; function App() { const BlueText = styled.p` color: blue; `; return ( <div> <BlueText>My blue text</BlueText> </div> ); } export default App;
But they also allow you to target other styled components (like if you were using CSS selectors):
import React from "react"; import styled from "styled-components"; function App() { const ImportantText = styled.div` font-weight: bold; `; const Text = styled.div` color: gray; ${ImportantText} { font-style: italic; } `; return ( <div> <Text> Text in gray <ImportantText>Important text in gray, bold and italic</ImportantText> </Text> <ImportantText>Important text bold</ImportantText> </div> ); } export default App;
This is useful when it is combined with pseudo-classes; for example, to change the color of a component on hover:
import React from "react"; import styled from "styled-components"; function App() { const ImportantText = styled.div` font-weight: bold; `; const Text = styled.div` color: gray; &:hover ${ImportantText} { color: red; } `; return ( <div> <Text> Text in gray <ImportantText>Important text in gray, bold and italic</ImportantText> </Text> <ImportantText>Important text bold</ImportantText> </div> ); } export default App;
Let’s say you’ve used Aphrodite to style your application and now you need to support themes.
The problem is that Aphrodite doesn’t support theming easily. At least, not as easy as Emotion does.
However, there are two projects that bridge the core of JSS with Aphrodite and styled-components: aphrodite-jss and styled-jss.
This way, you can keep the good parts of Aphrodite (or styled-components) and use all the features and plugins of JSS, from rule caching to rule isolation, and for themes, the theming package, which provides the following high-order components:
ThemeProvider
, which passes a theme object down the React tree by contextwithTheme
, which allows you to receive a theme object and it updates as a propertyFor example:
import React from "react"; import { createUseStyles, ThemeProvider, useTheme } from "react-jss"; function App() { const useStyles = createUseStyles({ wrapper: { padding: 40, background: ({ theme }) => theme.background, textAlign: "left" }, title: { font: { size: 40, weight: 900 }, color: ({ theme }) => theme.color }, link: { color: ({ theme }) => theme.color, "&:hover": { opacity: 0.5 } } }); const Comp = () => { const theme = useTheme(); const classes = useStyles({ theme }); return ( <div className={classes.wrapper}> <h1 className={classes.title}>Hello There!</h1> </div> ); }; const theme = { background: "blue", color: "white" }; return ( <div> <ThemeProvider theme={theme}> <Comp /> </ThemeProvider> </div> ); } export default App;
In the particular case of Aphrodite and themes, as another example, you can also use react-with-styles, which interfaces with Aphrodite and JSS, among others, to access theme information when defining styles.
Unlike inline styles, CSS-in-JS allows you to define animations using keyframes.
For example, this is how it’s done with styled-components:
import React from "react"; import styled, { keyframes } from "styled-components"; function App() { const MoveAnimation = keyframes` 0% { transform: translate(0, 0); } 50% { transform: translate(50px, 0); } `; const MyComponent = styled.div` display: inline-block; margin: 50px; width: 200; position: relative; animation: ${MoveAnimation} 1.5s ease infinite; `; return ( <div> <MyComponent>Hello There!</MyComponent> </div> ); } export default App;
But what not many people know is that you can chain multiple animations by using more than one keyframe object in the animation
property.
Here’s the above example modified to combine two animations:
import React from "react"; import styled, { css, keyframes } from "styled-components"; function App() { const MoveAnimation = keyframes` 0% { transform: translate(0, 0); } 50% { transform: translate(50px, 0); } `; const ColorAnimation = keyframes` from {color: red;} to {color: blue;} `; const MyComponent = styled.div` display: inline-block; margin: 50px; width: 200; position: relative; animation: ${(props) => css` ${MoveAnimation} 1.5s ease infinite, ${ColorAnimation} 1.5s linear infinite `}; `; return ( <div> <MyComponent>Hello There!</MyComponent> </div> ); } export default App;
Everything in CSS is global, and one of the purposes of using CSS-in-JS is to eliminate global style definitions.
However, there may be valid uses of global styles; for example, when you want to apply the same font styles to every element in your page.
Of course, you can always use traditional CSS, importing it via Webpack or declaring it in the index.html
file.
But if you’re serious about using JavaScript for all your styles, some libraries actually allow you to define global styles via helper components or extensions/plugins.
In Radium, you can use the Style component to render a styled element with global styles.
For example:
<Style rules={{ body: { fontFamily: 'Arial, Helvetica, sans-serif' } }} />
Will return:
<style> body { font-family: 'Arial, Helvetica, sans-serif'; } </style>
JSS uses a plugin to write global styles:
const styles = { '@global': { body: { fontFamily: 'Arial, Helvetica, sans-serif' } } }
Which will return:
body { font-family: 'Arial, Helvetica, sans-serif'; }
And in Aphrodite, you can use a third-party extension to create styles. For example:
import {injectGlobalStyles} from "aphrodite-globals"; injectGlobalStyles({ "body": { fontFamily: 'Arial, Helvetica, sans-serif', } });
This will return:
body { font-family: 'Arial, Helvetica, sans-serif'; }
Some libraries contain utilities for testing components with styles.
Aphrodite provides an undocumented (at least at the time of writing this) object, StyleSheetTestUtils, which is only available for non-production environments (process.env.NODE_ENV !== 'production'
) and has three methods:
suppressStyleInjection
, which prevent styles from being injected into the DOM, and it’s useful when you want to test the output of Aphrodite components when you have no DOMclearBufferAndResumeStyleInjection
, which does the opposite of suppressStyleInjection
and should be paired with itgetBufferedStyles
, which returns a string of buffered styles that have not been flushedHere’s an example of how they are used:
import { StyleSheetTestUtils, css } from 'aphrodite'; //... beforeEach(() => { StyleSheetTestUtils.suppressStyleInjection(); }); afterEach(() => { StyleSheetTestUtils.clearBufferAndResumeStyleInjection(); }); test('my test', () => { const sheet = StyleSheet.create({ background: { backgroundColor: 'blue' }, }); css(sheet.background); const buffer = StyleSheetTestUtils.getBufferedStyles(); });
Radium is another example. It has a TestMode object for controlling internal state and behavior during tests with the methods clearState
, enable
, and disable
.
Here, you can find an example of how TestMode is used.
CSS-in-JS is a technique for styling applications with JavaScript, and you can do interesting things with the libraries that implement it.
In this post, I have shown you five things that probably you didn’t know you can do with some of these libraries. Of course, not all libraries are created equal, and some things only apply to specific libraries.
Check out this playground where you can test and compare many CSS-in-JS libraries.
On the other hand, there are other libraries that are taking the concept of CSS, JavaScript, and types a little bit further.
One of these libraries is stylable, a component-based library with a preprocessor that converts Stylable’s CSS into minimal and cross-browser vanilla CSS.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
Handle frontend data discrepancies with eventual consistency using WebSockets, Docker Compose, and practical code examples.
Efficient initializing is crucial to smooth-running websites. One way to optimize that process is through lazy initialization in Rust 1.80.