React applications are being composed of small components that can be used individually and in best case scenario can be reused across multiple applications. But what about the CSS that they need for layouting elements inside them?
Often you end up inventing a system for it, something like that: you group CSS rules by a class name for each component and everything that is specific to a component goes in there. It's a start but it's not perfect. Soon you start renaming stuff or you want to apply styles from a global perspective.
Coming from the Vue.js world, I especially liked the approach of Single File Components - everything that belongs to that component goes into one file, CSS, HTML and JavaScript or TypeScript and I wanted to have this in React too, so I took of into the world of CSS-in-JS.
CSS Modules
I came to React, looking for Single File Components all over the place and as it turns out, it's not that easy π What I did find though, is CSS Modules π₯³
It works like this: you import the CSS as a JavaScript module , which have been mapped from your CSS class names and assign those as className
properties in the JSX. I used the npm package typescript-plugin-css-modules
for this. This is how a Component styled with it looks like:
import styles from "./foo.css";
const FooComponent = () => {
return <div className={styles.myClassName}>Hello, World!</div>;
};
I still wasn't satisfied with this approach as there were still two files to edit when the component needs to be modified. Then I learned about Emotion in Jason Lengstorf's Introduction to Gatsby course on Frontend Masters and it was exactly what I was looking for. I was intruiged π
Emotion to the rescue
To style React components with emotion, there are several options to choose from depending on your preferences and what you want to achieve. The one I like most as a starting point, is using the css
-template string helper that lets you write CSS like you would in a CSS file. This is a sample component using it to set a width
, height
and background-color
:
/** @jsxImportSource @emotion/react */
import { css } from "@emotion/react";
import React from "react";
function Card() {
return (
<div
css={css`
width: 100px;
height: 100px;
background-color: red;
`}
>
using the css template string helper
</div>
);
}
export default Card;
Really simple, huh? The first line /** @jsxImportSource @emotion/react */
tells the TypeScript compiler, how to resolve the css helper and it took me quite a while to figure it out!
But it can get quite messy when the component grows and contains more tags than just this one div. For this occasion, you can refactor the component and use styled components like this:
import styled from "@emotion/styled";
const ListItem = styled("li")`
font-weight: bold;
`;
interface ListProps {
items: Array<string>;
}
function List({ items }: ListProps) {
return (
<ul>
{items.map((item) => (
<ListItem key={item}>{item}</ListItem>
))}
</ul>
);
}
export default List;
As you can see, ListItem
uses the styled
function to create a styled component that only adds CSS to a li
element and automatically renders its children in it.
Now I got to the point where I was satisfied. At least until I realized that I wanted to have kind of like a theming, where I would store colors, definitions of borders and such things that I would need over and over again in a central location. Emotion provides a Theming API, why not try that?
Theming
To start with the theme, I implemented a new class for it and created a new instance of it:
class Theme {
readonly primaryColor: string = "green";
}
const theme = new Theme();
Theming then works like this: you provide the Theme to your components using a <ThemeProvider>
and access the theme in the actual component using the useTheme
hook provided by @emotion/react
. Here's my App, that does exactly that:
import { ThemeProvider } from "@emotion/react";
function App() {
return (
<div>
<ThemeProvider theme={theme}>
<MyComponentWithTheme />
</ThemeProvider>
</div>
);
}
And here is MyComponentWithTheme
, which uses both the Theme and the css
template string helper:
/** @jsxImportSource @emotion/react */
import { css } from "@emotion/react";
import { useTheme } from "@emotion/react";
import { Theme } from "../App";
function ComponentWithTheme() {
const theme = useTheme() as Theme;
return (
<div
css={css`
width: 100px;
height: 100px;
background: ${theme.primaryColor};
`}
>
Component using the Theme provided in App
</div>
);
}
export default ComponentWithTheme;
Using that, I found a way to write the CSS I need for my components directly in the components. I still need to figure out what parts of the style sheets go into a component or into a global style sheet, but it's a start.
Of course emotion does a lot more than that (like adding vendor prefixes and stuff) but I am still learning about it and find it very interesting and fun. I am looking forward for your tips and tricks around emotion and CSS-in-JS in common!
Top comments (3)
Thanks for your input! I am still a learner in frontend tech and haven't worked on large projects (yet). So would you advise to start with CSS Modules or go for CSS-in-JS only when the project won't become large anyways?