It's important that our components pass the tests, work well and don't re-render the screen too many times, but it's also important that they are visually attractive, right?
In this guide you'll find
First let's check we know some knowledge about how style and React works together:
- Every CSS file will be placed into the
<head>
, even if you have multiple CSS files across your components and containers. Because of that remember that in regular CSS the scope is global, take care with name repetition. - In JSX you must use the keyword
className
to assign CSS classes,class
is a special keyword from JS. - You can use both CSS and Sass, check the corresponding chapter.
- Vendor prefixes can be done automatically with PostCSS plugins or out of the box with CRA.
In React we have different ways to apply CSS to our project/components, here are some of the many options:
CSS
For apply style to our components the common pattern is create a separate .css
file in the same folder of the component/container with the same name.
|- components
|-- MyComponent
|--- MyComponent.js
|--- MyComponent.css
|--- index.js
import React from 'react';
import './MyComponent.css';
const MyComponent = () => {
return <h1 className="my-component-style">MyComponent</h1>;
};
export default MyComponent;
Remember that event if you declare classes in this separate file, Webpack/Parcel will place this classes into the head of the HTML and will be globally accesible, take care of using unique names.
Dynamically assign classes
The className
attribute search for a string of one or more classes to apply.
const classes = ['red', 'bold'].join(' '); // 'red bold' is a valid CSS
<p className={classes}>Test</p>;
Sass
Sass is a CSS preprocessor, which allows us to use features that don’t exist in CSS like nesting, mixins, inheritance, etc.
The .sass
and .scss
files can be used without any configuration with create-react-app (version 2 and above). Create files with that extension and will work like you saw in the previous section.
If you're not using create-react-app
or my Simple React Boilerplate you must install node-sass to your project.
Node-sass is a library that provides binding for Node.js to LibSass, the C version of the popular stylesheet preprocessor, Sass.
It allows you to natively compile .scss files to css at incredible speed and automatically via a connect middleware.
yarn add node-sass
CSS Modules
With CSS modules, you can write normal CSS code and make sure, that it only applies to a given component/container.
If you use create-react-app
(version 2 and above) CSS modules are already enabled, but in order to use it you must follow the naming convention MyComponent.module.css
.MyComponent {
text-align: center;
}
import React from 'react';
import classes from './MyComponent.module.css';
const MyComponent = () => {
return <div className={classes.MyComponent}>
};
export default MyComponent;
With this the class name will look like MyComponent__MyComponent__c7e
in the final product, that unique name is attached to this component. If you want to work with global classes you only need to add :global
:global .MyComponent {
text-align: center;
}
import React from 'react';
import classes from './MyComponent.module.css';
const MyComponent = () => {
return <div className="MyComponent">
};
export default MyComponent;
Once CSS Module is imported with classes
name or any other name you want, is treated as a JS object.
All of this is applicable to Sass
too, you only need to change the file extension to .sass
or .scss
.
If you want to learn more about CSS Modules I recommend this article by CSS Tricks
CSS-in-JS
As the name suggest CSS-in-JS is a pattern where you build the CSS directly on the JS, for that reason the properties are camelCase
because we're working with JS objects.
.my-div {
border-bottom: 1px solid teal;
}
<div style={{borderBottom: '1px solid teal'}} />
The upside of writing CSS in line is the scope. The borderBottom
now is scoped to that only div
for that you won't have any collision with other CSS classes. But if is now scoped what happens if I want to reuse?
The style
attribute search for a JS object.
const MyComponent = () => {
const style = {
backgroundColor: 'red',
color: 'white',
font: 'inherit',
border: '1px solid blue',
padding: '8px',
cursor: 'pointer',
margin: '0 5px auto'
};
style.backgroundColor = 'green';
return (
<div>
<p style={style}>We have the same style</p>
<p style={style}>We have the same style</p>
</div>
);
};
A major downside of inline styles is some powerfull tools present in CSS, like pseudo selectors, you can't use it in this way.
Radium
As we saw above the real problem is, if in the CSS of your component defines:
button:hover {
color: black;
}
This will affect every button on your app because remember, it's in a global scope.
In order to use pseudo selector and other features in in-line style, you must install a third party package:
yarn add radium
For use Radium
in your component besides of importing it, you must wrap your export:
import Radium from 'radium';
export default Radium(App);
With that now we can use Radium features like the hover:
const style = {
backgroundColor: 'red',
color: 'white',
font: 'inherit',
border: '1px solid blue',
padding: '8px',
cursor: 'pointer',
margin: '0 5px auto',
':hover': {
backgroundColor: 'salmon',
color: 'black'
}
};
style.backgroundColor = 'green';
style[':hover'] = {
backgroundColor: 'lightgreen',
color: 'black'
};
Remember that the properties of a JS object can be defined with strings too, normally use this way if contain invalid caracters (like :
). Later on, when you want to manage that property, must be used with []
.
Using media queries
Radium enables to use media queries in inline CSS but you need to wrap you application into a StyleRoot
.
import Radium, { StyleRoot } from 'radium';
return (
const style = {
'@media (min-width: 500px)': {
width: '450px'
}
};
<StyleRoot>
...
</StyleRoot>
);
Styled Components
styled-components use tagged template literals which are a feature of JS ES6, to write CSS (even pseudo selectors, mediaqueries, etc.) directly in your JS file, locally scoped and autoprefixed.
yarn add styled-components
import React from 'react';
import styled from 'styled-components';
const MyComponent = () => {
return (
<StyledDiv>
<span className="my-styled-class">Hello!</span>
</StyledDiv>
);
};
const StyledDiv = styled.div`
margin: 0 auto;
border: 1px solid teal;
.my-styled-class {
color: papayawhip;
}
.my-styled-class:hover {
color: palevioletred;
}
`;
export default MyComponent;
I recommend to use this VSCode plugin which higlights correctly the CSS inside tagged template literals.
Dynamic Styles
styled components also accepts functions/props to make truly dynamic styles.
import React from 'react';
import styled from 'styled-components';
const MyComponent = () => {
return (
<StyledDiv primary>
<span className="my-styled-class">Hello!</span>
</StyledDiv>
);
};
const StyledDiv = styled.div`
margin: 0 auto;
border: 1px solid ${props => props.primary ? 'teal' : 'black'};
.my-styled-class {
color: papayawhip;
}
.my-styled-class:hover {
color: palevioletred;
}
`;
export default MyComponent;
My personal approach to organize styles and components is the following:
|- components
|-- MyComponent
|--- MyComponent.js
|--- MyComponentBase.js
|--- index.js
-
index.js
will import/exportMyComponent.js
, is a good practice and is easy when you want to import the component. -
MyComponent.js
will hold the styled version ofMyComponentBase.js
. -
MyComponentBase.js
will have all the magic, like any other regular React component.
There is one downside tho, most of the automatic documentation libraries like React Docgen will only see the prop types present in the MyComponentBase.js
.
Tailwind CSS
With Tailwind CSS you can create your components from scratch rapidly. You will use a bunch of classes that closely map to underlying CSS properties.
Using this CSS framework will take some learning but if you already know CSS it will be easy, for example this two divs will get the same style:
<div style={{
height: '16px',
width: '16px',
textAlign: 'center'
}}></div>
<div className="h-16 w-16 text-center"></div>
I do not recommend this option if you're totally new in CSS, first learn CSS properly and then jump into this framework for simplicity and velocity.
Combine with React
The easiest way is to import from the CDN directly to your index.html
place the link
inside the head
.
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/tailwindcss@0.7.4/dist/tailwind.min.css"
/>
EXTRA
React Spring
I want to add React Spring to this guide because it's an amazing animation library and brings nice and fluid animations to your project.
yarn add react-spring
With React Spring you will use the hooks that library provides to implement animations, instead of writing complex CSS animations with durations and curve effects you only need to write which prop want to animate.
const props = useSpring({
opacity: 1,
from: { opacity: 0 },
})
return <animated.h1 style={props}>hello</animated.h1>
The animation is not based on a defined curve or a set duration, under the hood it implements spring
to bring natural animations.
Be sure to check the official github repo.
Conclusion
Styling in React can be done in multiple ways, which one to use is totally up to you if you are aware of the scope of the CSS classes, but here are my personal preferences:
- For components in a UI library that other projects will consume -> styled-components (CSS locally scoped, easy to open the door for theming with regular class names, automatically vendor prefixed)
- For a project which later will be maintained for other multidisciplinare members -> CSS/Sass modules (Locally scope, treat as JS object, easy to change in the JS logic without changing anything from the
.css
file) - Working alone and/or fast prototyping -> Tailwind CSS (I left this option for working alone because if other team members are not aware of the Tailwind keywords can be confusing)
So what are you favorite way of doing styling in React? There are a lot of options.
Top comments (8)
Where do the props come from for the dynamic styled-component for
props => props.primary
?Like any other React component, under the hood styled-component is creating a valid React component which you will use in JSX.
If you use my approach to divide the styled and the logic in two different files (with MyComponent.js and MyComponentBase.js) all props go through both JS and you can decide where to use them.
But you don't have to pass them in to a function manually? They're just automatically available on the styled.div template string as a "props" object?
That's correct, they're available on the styled.div because you pass the props when you use the component in the JSX, check the example provided in the official webpage: styled-components.com/
Thanks, I see now. They're the actual props passed to the styled component. Since the library creates components and not just styles
Wow, great article! It is necessary to try somehow useSpring in business :)
Mr. Dastasoft, I like the module tip because that tip stops my dependency tree from exploding and then I can simply code more fearlessly. 🌱🌱🌱
I think Tailwind is not for working alone. I think it is the better option if you can decide what is member or not in the team. :D