Best Ways to Style Components in React

7 min read
Cover Image for 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.

BEM - Block Element Modifier