130

I have several components which have the following CSS/component structure

About/style.css

.AboutContainer {
    # Some style
}

p > code {
    # Some style
}

And I import the CSS in the componet as follows

About/index.js

import './style.css';

export default class About extends Component {
    render() {
         # Return some component
    }
}

However, the CSS is imported in the <header> section and stays global-scope.

I was expecting CSS to be:

  1. Component-scoped in a way that the style is only applied to things that are only rendered within this component.
  2. Style for this component would disappear if the component is unmounted.

However, when inspecting from the browser, the styles are specified at the <header> section and gets applied to all the components

<header>
   // Stuff
   <style type="text/css">style for component About</style>
   <style type="text/css">style for component B</style>
   <style type="text/css">style for component C</style>
   // Stuff
</header>

How do I import CSS to be component-scoped? It seems like I'm understanding CSS import in React ES6 incorrectly.

I was following this tutorial


Edit

Answer by Brett is correct. However, my problem turns out to be somewhere else. I created my app using create-react-app which basically simplifies setups required to do React. It include WebPack, Babel and other things to get started. The default WebPack config that it uses did not set module option for the css-loader so it defaulted to false, and as a result the local-scoping was not enabled.

Just for additional info, it seems like create-react-app does not have straightforward way to customize WebPack config, but there seem to be numerous how-to workarounds on the web.

4
  • 1
    Take a look at CSS modules - github.com/css-modules/css-modules Commented Nov 3, 2017 at 7:33
  • 11
    Something is broken at Stackoverflow, when a well-formulated question gets answered by "this shiny library solves your problems", without addressing the problem. The question is updated with the right answer (thanks for that @Harry Cho!) but gets zero upvotes. Commented Jun 30, 2018 at 9:08
  • You can use css modules with create-react-app without ejecting according to this blog : robinwieruch.de/create-react-app-css-modules Commented Jan 23, 2019 at 14:48
  • 2
    rename mystyle.css to mystyle.module.css and import it to use css modules in a react app created using create-react-app Commented Oct 9, 2019 at 14:06

7 Answers 7

82

It sounds like CSS Modules, or many of the other CSS-in-JS packages, does what you want. Others include Emotion (my current favorite), Styled Components, or many of the packages here.

A CSS Module is a CSS file in which all class names and animation names are scoped locally by default. All URLs (url(...)) and @imports are in module request format (./xxx and ../xxx means relative, xxx and xxx/yyy means in modules folder, i. e. in node_modules).

Here's a quick example:

Let's say we have a React component like:

import React from 'react';
import styles from './styles/button.css';

class Button extends React.Component {
  render() {
    return (
      <button className={styles.button}>
        Click Me
      </button>
    );
  }
}
export default Button;

and some CSS in ./styles/button.css of:

.button {
  border-radius: 3px;
  background-color: green;
  color: white;
}

After CSS Modules performs it's magic the generated CSS will be something like:

.button_3GjDE {
  border-radius: 3px;
  background-color: green;
  color: white;
}

where the _3DjDE is a randomly generated hash - giving the CSS class a unique name.

An Alternative

A simpler alternative would be to avoid using generic selectors (like p, code, etc) and adopt a class-based naming convention for components and elements. Even a convention like BEM would help in preventing the conflicts you're encountering.

Applying this to your example, you might go with:

.aboutContainer {
  # Some style
}

.aboutContainer__code {
  # Some style
}

Essentially all elements you need to style would receive a unique classname.

Sign up to request clarification or add additional context in comments.

Comments

26

Maybe react-scoped-css will help. Btw, I'm the author of this lib, if you find anything broken or simply want to improve it, you can always raise an issue or send a pr.

1 Comment

After hours of debugging, I realized that this package is not compatible with framer-motion components, e.g. motion.button, motion.input etc.
19

Because you mentioned you used create-react-app, the solution here is quite easy change just style.css to style.module.css, it will look like this:

import styles from "./style.module.css"
<button className={styles.button}>blabla</button>

More info on this article: https://blog.bitsrc.io/how-to-use-sass-and-css-modules-with-create-react-app-83fa8b805e5e

Comments

12

You can use SASS (.scss) to imitate scoped CSS.

Say you need to use bootstrap in only one component (to avoid conflicts). Wrap the component in <div className='use-bootstrap'> and then created a .scss file like so:

.use-bootstrap {   
  // Paste bootstrap.min.css here
}

3 Comments

that does not resolve the whole issue - the CSS selectors will be concatenated (if sass nesting is used), but what about the markup? inner selectors are still in danger of being not unique
this would work, but the relevant paths used for @font-face would result in errors in console...
this worked fine for me. I included the hole Bulma framework using .use-bulma { @import "bulma"; }
8

Use this file naming convention [name].module.css and see documentation: https://create-react-app.dev/docs/adding-a-sass-stylesheet

JSX File

import React from 'react';
import styles from './MyPage.module.scss';

const MyPage = () => {
    return (
        <div className={styles.myDiv}>
            My Page
        </div>
    );
};

export default MyPage;

Styles File

    .myDiv {
        color: #f3f3f3;
        font-family: "Cambria";
        font-weight: normal;
        font-size: 2rem;
    }

Comments

2

For me, the simple solution (without using: Css-modules or css-in-js) is to add a suffix to your class selectors like this:

className="btn__suffix"

if your component is named: FileUpload.tsx so your __suffix would be __fu, i took the first character of each word (here: File and Upload).

the end result would be:

import './style.css';

export default class About extends Component {
render() {
      Return (
         <div className="container__fu">
           ...
         </div>
              )
   }
}

And in the Css part, your file would be:

.container__fu {
   ...
}

Comments

0

Create a wrapper class:

<div className='wrapper'>...</div>

and with SASS you can load styles inside the wrapper:

@use 'sass:meta';

.wrapper {
  @include meta.load-css('@author/library/index.css'); // or local style
}

The biggest advantage of this method is that if the author of the library has also included styles from external libraries (e.g. the entire Bootstrap) our current ones will not be overwritten outside this .wrapper scope.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.