Best Ways to Style Components in React
There are different ways of styling our components, each having its own advantages and disadvantages. For this article we just will compare 2 options that meet good standards, as well as some of the best practices.
01 Using Sass
Sass is a CSS preprocessor that has features that don't exist in CSS yet like nesting, mixins, inheritance, and other nifty goodies that help you write robust, maintainable CSS.
Variables
We can use variables to store things like colors, font stacks, or any CSS value you think you'll want to reuse. Sass uses the $ symbol to make something a variable. Here's an example:
Variables Example Code
$font-stack: Helvetica, sans-serif;
$primary-color: #333;
body {
font: 100% $font-stack;
color: $primary-color;
}
Nesting
Sass will let you nest your CSS selectors in a way that follows the same visual hierarchy of your HTML. Be aware that overly nested rules will result in over-qualified CSS that could prove hard to maintain and is generally considered bad practice.
Nesting Example Code
nav {
ul {
margin: 0;
padding: 0;
list-style: none;
}
li { display: inline-block; }
a {
display: block;
padding: 6px 12px;
text-decoration: none;
}
}
Modules
We don't have to write all our Sass in a single file, we can split it up however you want with the @use rule. This rule loads another Sass file as a module, which means you can refer to its variables, mixins, and functions in your Sass file with a namespace based on the filename.
Module Example Code (_base.scss)
// _base.scss
$font-stack: Helvetica, sans-serif;
$primary-color: #333;
body {
font: 100% $font-stack;
color: $primary-color;
}
Use Module Example Code (styles.scss)
// styles.scss
@use 'base';
.inverse {
background-color: base.$primary-color;
color: white;
}
Mixins
A mixin lets you make groups of CSS declarations that you want to reuse throughout your site. You can even pass in values to make your mixin more flexible.
Mixin Example Code
@mixin theme($theme: DarkGray) {
background-color: $theme;
box-shadow: 0 0 1px rgba($theme, .25);
color: #fff;
}
.info {
@include theme;
border-radius: 8px;
Getting Started
In order to start using SASS, you have to install:
$ npm install node-sass --save-dev
$ # or
$ yarn add node-sass --dev
Now you can rename src/App.css
to src/App.scss
and update src/App.js
to import src/App.scss
.
This file and any other file will be automatically compiled if imported with the extension .scss
or .sass
.
For more information about this way, you can find it at https://sass-lang.com/guide.
02 Using CSS-in-JS
We can make style in javascript using the "makeStyles" or "createStyles" class of material UI that comes with great features too (theme nesting, dynamic styles, self-support, etc.).It uses a high-performance JavaScript to CSS compiler which works at runtime and server-side- It is faster and less than 15 KB gzipped.
There are 3 possible APIs that you can use to generate and apply styles, however, they all share the same underlying logic:
Hook API
Using a class name like we do normally on CSS but using javascript.
Hook API Example Code
import * as React from 'react';
import { makeStyles } from '@mui/styles';
import Button from '@mui/material/Button';
const useStyles = makeStyles({
root: {
backgroundColor: '#FE6B8B',
},
});
export default function Hook() {
const classes = useStyles();
return <Button className={classes.root}>Hook</Button>;
}
Styled components API
Basically, we can extend a component or a tag, put some style and use this new component as a tag.
Styled components API Example Code
import * as React from 'react';
import { styled } from '@mui/styles';
import Button from '@mui/material/Button';
const MyButton = styled(Button)({
backgroundColor: '#FE6B8B',
});
export default function StyledComponents() {
return <MyButton>Styled Components</MyButton>;
}
Higher-order component API
We can create a variable with all styles wanted and pass through props to a component and use it as classes.
Higher-order component API Example Code
import * as React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '@mui/styles';
import Button from '@mui/material/Button';
const styles = {
root: {
backgroundColor: '#FE6B8B',
},
};
function HigherOrderComponent(props) {
const { classes } = props;
return <Button className={classes.root}>Higher-order component</Button>;
}
HigherOrderComponent.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(HigherOrderComponent);
However, in the 3 API's we can use features such as variables
Variables
Like in our typically JS code we can create a variable and passthrough into our style:
Variables Example Code
import * as React from 'react';
import { makeStyles } from '@mui/styles';
const redColor = '$FF0000';
const useStyles = makeStyles({
root: {
backgroundColor: redColor,
},
});
The problem is that we have to export these variables in order to use them in other style files
Nesting:
Nesting Example Code
import * as React from 'react';
import { makeStyles } from '@mui/styles';
const useStyles = makeStyles({
root: {
backgroundColor: 'red',
'&.selected $element': {
padding: 20, //stands for '20px'
backgroundColor: 'blue',
'& span': {
color: 'green'
},
}
}
});
Nesting classes on HTML Example Code
import classNames from 'classnames';
...
<div className={classNames(classes.root, elementSelected ? 'selected : '')}>
<div className={classes.element}>
<span>Hi :)</span>
</div>
</div>
Adapting based on props
It's possible to adapt our style based on props.
Adapting based on props Example Code
import * as React from 'react';
import { makeStyles } from '@mui/styles';
const useStyles = makeStyles({
// Style rule
foo: (props) => ({
backgroundColor: props.backgroundColor,
}),
});
function MyComponent() {
// Simulated props for the purpose of the example
const props = {
backgroundColor: 'black',
color: 'white',
};
// Pass the props as the first argument of useStyles()
const classes = useStyles(props);
return <div className={`${classes.foo} ${classes.bar}`} />;
Using the theme context
We also can create a theme and use it whatever we want by import in ThemeProvider component available in @mui/styles.
Theme context Example Code
import * as React from 'react';
import { makeStyles } from '@mui/styles';
import { createTheme, ThemeProvider } from '@mui/material/styles';
const theme = createTheme();
const useStyles = makeStyles((theme) => {
root: {
color: theme.palette.primary.main,
}
});
const App = (props) => {
const classes = useStyles();
return <ThemeProvider theme={theme}><Container>...</Container></ThemeProvider>;
}
You also can find a default mui theme example here.
Mixins (undocumented)
Exist a way, unrecord, to use mixins on CSS-in-JS.
Theme context Example Code
import * as React from 'react';
import { createTheme } from '@material-ui/core/styles';
import { makeStyles } from '@mui/styles';
import { MixinsOptions } from '@material-ui/core/styles/createMixins';
// interface declaration merging to add custom mixins
declare module '@material-ui/core/styles/createMixins' {
interface Mixins {
defaultButton: CSSProperties, // <- custom mixin!
}
}
const mixins: MixinsOptions = {
defaultButton: {
backgroundColor: 'green',
borderRadius: 8,
}
};
const theme = createTheme({ mixins });
const useStyles = makeStyles((theme) => {
button: {
color: 'red',
...theme.mixins.defaultButton,
}
});
const classes = useStyles();
return <button className={classes.button}>Click me!</button>;
Getting Started
In order to start using CSS-in-JS, you have to install:
$ npm install @mui/styles --save-dev
$ # or
$ yarn add @mui/styles --dev
Now to use a styled theme you will need to provide the theme as part of the context. For this, you can use the ThemeProvider component available in @mui/styles, or, if you are already using @mui/material, you should use the one exported from @mui/material/styles so that the same theme is available for components from '@mui/material'. Exactly how we did above on "Using the theme context" example.
For more information about this way, you can find it at https://mui.com/styles/basics.
Style Architecture Best Practices
PracticesIn both cases, whatever the decision, we must adopt a nice file structure to maintain the code quality.
For an example:
styles/
|
|-- utilities/
| - _variables.scss/.ts
| - _mixins.scss/.ts
| - _extends.scss/.ts
|
|- reset/
| - _reset.scss/.ts
| - _typography.scss/.ts
|
|- layout/
| - _example1_layout.scss/.ts
| - _example2_layout.scss/.ts
|
|- third-party/
| – _bootstrap.scss/.ts
|
- main.scss/.ts
Not just the files structure, but how you organize your code is the key to efficiency in at least these three ways:
it affects how long it takes you to write code
how much of that code you’ll have to write
how much loading your browser will have to do.
This becomes especially important when you’re working in a team and when high performance is essential.
If your option goes to use SASS, then you should look at BEM (Blocks, Elements and Modifiers) pattern.