Styling in React
Are you CSS in JS or CSS out JS? 🎨 permalink
Styling is a huge part of the user experience for your apps. You’ve got a new design and you’re going to build your new awesome app in React
. But how do you go about implementing that design?
It would be fair to say that React
is pretty popular. So it’s no surprise that there are a multitude of options when it comes to styling React
apps. There is no real right or wrong. It’s what feels best for you. The main decision will be going in or out. And by that I mean “CSS in JS” or “CSS out JS” 💅 Of course, if you’re styling a React Native
app, that decision has already been made for you.
For those in camp TL;DR, There are many options when it comes to styling your React
apps. Go with what feels best for you. Whether you go CSS
in or out, I recommend colocating styles with their components 🙏 Be open to trying out new techniques and packages. You might surprise yourself. For more on styling React
apps be sure to check out the Styling and CSS FAQ in the docs!
General structure and approach permalink
Before we dig into some of the options let’s consider structure and approach. We won’t dig into this too much I promise.
In general we should try to colocate styles with their respective components. This can make life a lot easier when your projects become complex. It’s also easier for those new to a project to understand where things live. If you’re opting to go the “CSS
in JS
” route, then you can write your styles in the component file. I would recommend extracting styles into their own files though. Smaller files are going to be easier to read and digest; especially for newcomers to a project.
One approach I like is to use an extension prefix. Consider a Nav
component. I would create a folder for Nav
inside a Components
folder. I could then create index.js
and style.js/css
. You could go a step further with nav.style.js
. This makes things like searching for a file with an IDE much easier in my experience.
Aside from structure, it should go without saying that it’s wise to try and reuse styles where possible. Extract common styles into their own stylesheets or modules. Consuming reusable patterns will reduce code bloat and make maintenance easier.
With that out of the way, let’s move onto our options! 👍
The main options fall into two categories. CSS
in JS
or CSS
out of JS
. Both will work great!
Good ‘ole CSS 👍 permalink
Let’s start with CSS
. It’s likely to be your first port of call and what you’re familiar with. Using CSS
will entail assigning classes to your components using the className
prop.
const Emoji = ({ children, large }) => (
<span className="emoji" role="img">
{children}
</span>
)
Classes and classnames permalink
As with any project, you’ll want to spend some time considering appropriate classes. Opt for using a classing standard like BEM
. Not long into working with the className
prop, you’re likely to come across an issue. The issue being how to assign many classes based on state
, props
etc.
const Emoji = ({ children, large }) => (
<span className={['emoji', large ? 'emoji--large' : '']} role="img">
{children}
</span>
)
Using an Array
like this won’t work. You can use template literals of course.
const Emoji = ({ children, large }) => (
<span className={`emoji ${large ? 'emoji--large' : ''}`} role="img">
{children}
</span>
)
You could also opt to use Array.join
if you wanted 👍
const Emoji = ({ children, large }) => (
<span className={['emoji', large ? 'emoji--large' : ''].join(' ')} role="img">
{children}
</span>
)
Alternatively, you might find more joy using the classnames
package 👍
CSS Modules permalink
If you‘re not into using class name standards, CSS
modules might be right for you. With postcss-modules
we can use locally scoped animation and class names.
Writing something like
import { emoji, large } from './emoji.css
const Emoji = ({ children, large: isLarge }) => (
<span className={`${emoji} ${isLarge ? large : ''}`} role="img">
{children}
</span>
)
Will give us HTML
that uses locally scoped classes
<span class="_emoji_1fqtj_1 _large_1fqtj_4" role="img">😸</span>
Being performant + code splitting permalink
Although the majority of your app won’t be in the DOM
on load, that doesn’t mean CSS
performance rules don’t apply. You will still want to tackle render blocking CSS
. You can do this by inlining critical styles and only loading what’s necessary for the root of your app.
And that’s a great use case for utilising code splitting in your React
apps. With code splitting you will be able to do things like lazy load styles for routes within your app.
CSS in JS 😍 permalink
The alternative to CSS
out of JS
is to go CSS
in JS
✨
Whilst the majority of your styling is now defined in JS
, you will likely need to define some in CSS
. Inlining some critical styles and defining base styling will aid with page performance 👌 Unless you are using SSR
, there will still be a small delay between page load and app load. You can check performance for your app by running an Audit
through the developer tools in Chrome
.
Benefits of CSS in JS? permalink
You’ve opted to go CSS
in JS
. You are now defining your styles within JavaScript
. The major benefit being now that you can abstract styles to the component level. You can separate styles from logic within your components. This is important. You are getting the full power of JavaScript
to use within your styling.
Not only that, you will only create the styles you need and only load them when appropriate. You can even test your styles! There aren’t the only benefits.
How does it work? permalink
At it’s simplest, through inline styling. But, this is not usually how CSS
in JS
works. We are likely to use a third party package for our CSS
in JS
. These packages will do the heavy lifting for us 💪 In most cases, these packages will take our style definitions and create class names for them. These styles are then inlined within a style
tag inside the head
of the page document
. A benefit to this approach being that we aren’t duplicating styles. It also makes the DOM
easier to inspect.
Here’s a very simple example using glamorous
. AppHeading
isn’t very useful here but there for demo purposes.
const Heading = glamorous.h1({
fontSize: 100
})
const AppHeading = () => <Heading>Hi 👋</Heading>
This would create a h1
with a unique class
generated for it.
<h1 class="css-uhtiv5">Hi 👋</h1>
Note the class
name. A style rule will have been inlined 👍
<style>
.css-uhtiv5 { font-size: 100px; }
</style>
Before digging into third party CSS
in JS
, let’s roll back to creating it ourself to understand the core benefits.
CSS in JS using inline styles permalink
The premise of CSS
in JS
is exactly that. At it’s core, we define styling within JavaScript
using Object
s or template literals. A basic example being to create a style definition and apply it inline to an element. Unfortunately, we can’t pass an Array
of Object
s but we can pass a merging of Objects
.
import { emoji, large } from './emoji.style'
const Emoji = ({ children, large: isLarge }) => (
<span style={Object.assign({}, emoji, isLarge ? large : {})} role="img">
{children}
</span>
)
In this example, two styles emoji
and large
are imported. They are conditionally merged based on props.
How might those styles look? They could simply be as follows
const emoji = {
fontSize: '8rem'
}
const large = {
fontSize: '16rem'
}
Note, in most cases when defining styles within JavaScript
, we will use camelCase
. Numbers are likely to default to pixel
s too.
Those styles don’t look very efficient, do they? 👎
A major benefit of defining CSS
in JS
is to be able to conditionally generate styles. We can refactor our styles into a function that returns the style for our component 💪
const EmojiStyle = ({ large }) => ({
fontSize: large ? '16rem' : '8rem'
})
const Emoji = ({ children, large }) => (
<span style={EmojiStyle({ large })} role="img">
{children}
</span>
)
But, things are going to get pretty tedious pretty quick. That’s because we will be writing these styling functions over and over. And you’ll also have to keep up with maintaining them. You’re also inlining everything 😭
Third party packages example permalink
Luckily, there are some great third party packages to aid with handling CSS
in JS
😌
I don’t have experience with them all so I will be using styled-components
for these examples. It’s my personal favorite and the one I have most experience with 😅 But there are many options out there. Michele Bertoli has done a great job of curating them here 👏
Don’t worry though as I won’t dig too far into third party packages ⛏ This is because you’ll likely want to explore for yourself and create your own preference. We will run through a very simple example so we can see the benefits.
If you’d rather take a quick look at styled-components
before going any further, check it out here 👍 Don’t worry though, I will keep any examples intuitive and simple.
For a more thorough look at styled-components
, I’d strongly recommend watching this fantastic video
Let’s start simple. If we take our example from above, we could transform that into an Emoji
styled component.
const Emoji = styled.span`
font-size: ${p => p.large ? 12 : 8}rem;
`
Note how we are writing plain CSS
there 👍 styled-components
uses tagged template literals to support all CSS
. You can create animations, use pseudo elements, everything is vendor prefixed for you!
For our example, it should be clear what’s happening here and how we could use it.
const MyFavoriteEmojis = () => (
<Fragment>
<Emoji>😅</Emoji>
<Emoji>😎</Emoji>
<Emoji>👍</Emoji>
</Fragment>
)
Want to make one larger? Boom 💣 It’s that easy.
const MyFavoriteEmojis = () => (
<Fragment>
<Emoji>😅</Emoji>
<Emoji large>😎</Emoji>
<Emoji>👍</Emoji>
</Fragment>
)
You may notice something already. Not only do we get conditional styling but our styled component gives us context.
const MyFavoriteEmojis = () => (
<Fragment>
<span className="emoji" role="img">😅</span>
<span className="emoji" large role="img">😎</span>
<span className="emoji" role="img">👍</span>
</Fragment>
)
If we were to use span
s, we would lose that context. If we were to use our stateless inline Emoji
component from above, we wouldn’t get the maintenance benefits. Of course, with this example being very small, it’s may be hard to justify the switch. But think larger than that and you are likely to see the benefits.
Consider apps you have written that use several div
s within a component. Using a CSS
in JS
solution, we not only get the power of CSS
in JS
but we can provide context to smaller elements. Smaller elements whose sole purpose may be to provide style.
And to reiterate, this is important. We are able to separate styles from logic.
Consider the example of a sliding side bar navigation menu.
class SideBarNav extends Component {
render = () => {
const {
open
} = this.state
const {
items
} = this.props
return (
<nav className={`super-nav ${open ? 'super-nav--open' : ''}`}>
<ul className="super-nav__list">
{items.map(item => (
<li className="super-nav__item">
<label>{item.label}</label>
</li>
))}
</ul>
</nav>
)
}
}
There’s a bit going on there already without looking at styling. But consider that our CSS
will have to contain styling for when the nav is open and closed 👎
Using styled-components
we could create something more along the lines of
class SideBarNav extends Component {
render = () => {
const {
open
} = this.state
const {
items
} = this.props
return (
<Container open={open}>
<Items>
{items.map(item => (
<Item>
<ItemLabel>{item.label}</ItemLabel>
</Item>
))}
</Items>
</Container>
)
}
}
That is so much clearer 😍 Any styling declaration via classNames
are extracted and we are left with building blocks to create our component 👌
Our Container
component may look like this
const Container = styled.nav`
background: red;
left: 0;
position: absolute;
top: 0;
transform: translateX(${p => p.open ? 0 : -100}%);
transition: transform 0.25s;
width: 250px;
`
These styled components give us small manageable blocks to build our apps with.
Once you start digging into using CSS
in JS
you start to sense its benefits. The power of JavaScript
for your styling, better structure and context for your JSX
.
React Native permalink
It’s worth mentioning react-native
apps and how you might style them. With react-native
, you’re going CSS
in JS
. There is no notion of className
. Everything is inline through the style
property. But you can pass arrays of styles 😉
<TouchableOpacity style={[{
backgroundColor: 'red',
height: 100,
width: 200,
}, secondary ? { backgroundColor: 'blue' } : {}]}>
<View style={{
height: '100%',
width: '100%',
}}>
<Text style={{ fontFamily: 'Lato', fontSize: '24' }}>Hey 👋</Text>
</View>
</TouchableOpacity>
You’ll also get StyleSheet
. A helper for creating your react-native
styles.
const styles = StyleSheet.create({
button: {
backgroundColor: 'red',
height: 100,
width: 200,
},
secondary: {
backgroundColor: 'blue'
},
content: {
height: '100%',
width: '100%',
},
text: {
fontFamily: 'Lato',
fontSize: '24',
}
})
Which means you can tidy up a little like this
<TouchableOpacity style={
[styles.button, secondary ? styles.secondary : {}]}>
<View style={styles.content}>
<Text style={styles.text}>Hey 👋</Text>
</View>
</TouchableOpacity>
But if you wanted to have some consistency across platforms, you could continue with styled-components
! They have a native implementation which is pretty sweet if you ask me 👏
const Button = styled.TouchableOpacity`
background-color: ${p => p.secondary ? 'blue' : 'red'};
height: 100;
width: 200;
`
const ButtonContent = styled.View`
height: 100%;
width: 100%;
`
const ButtonText = styled.Text`
font-family: 'Lato';
font-size: 24;
`
Giving you a nice clean component structure of
<Button secondary={props.secondary}>
<ButtonContent>
<ButtonText>Hey 👋</ButtonText>
</ButtonContent>
</Button>
That’s it! permalink
If you’ve made it this far, thanks for reading 🙌 It’s not the easiest subject to write about but I hope you gained something from it.
We’ve taken a look at the whole CSS
in or out of JS
topic. For web applications, there does seem to be a shift towards going CSS
in JS
, at least for now. Regardless of which way you go you’ll still likely need to write or use some native CSS
at some point.
If you do find yourself making the switch, I recommend checking out styled-components
. It’s very easy to pick up.
As always, any questions or suggestions, please feel free to leave a response or tweet me 🐦!
Further Reading permalink
- Styling FAQ —
React
docs - Render blocking resources — Google Developers
- Render blocking CSS — Google Developers
- CSS in JS comparison — Michele Bertoli
- classnames package —
npm