11

I have a React component with the following files:

  • src/components/HomePage/index.js
  • src/components/HomePage/style.scss

The component is very simple:

import React from 'react';
import './style.scss';

const HomePage = () => {
    return (
        <div className="homepage">
            <h1>Landing page</h1>
        </div>
    );
};

export default HomePage;

Within style.scss I am applying a style to all <h1> tags:

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

And it works as expected. However, I now see that the h1 style within styles.scss is being applied to every h1 on my site, even on pages that do not use this component.

I am using Gatsby, but it's a React app at heart. My understanding is that React's code-splitting feature would take care of this, that the code from style.scss would only be included in bundles for any page that uses my component.

It's the why that I am asking about. I have two easy fixes:

  • Wrap everything in style.scss in a .homepage wrapper
  • Use CSS modules and rename the file to style.module.scss. When I see people do that they always do `import style from './style.module.scss' - is there a way to have CSS modules without assigning it to an object like that?

Update: Coming back to this question after spending a lot of time with React and I think there's a gap in the market for React styling. CSS modules is syntactically poor in my opinion and having to manually wrap everything in a .home tag to localise it is manual work I don't want to do. Someone should really create a React plugin that automatically does this so that whenever I have a file called Home.js and I import Home.css that all the CSS is automatically restricted to Home.js without me having to do anything special.

MSOACC
  • 3,074
  • 2
  • 29
  • 50
  • check this thread out https://stackoverflow.com/questions/47090574/how-to-make-react-css-import-component-scoped – martin Jul 25 '20 at 20:35
  • Does this answer your question? [How to make React CSS import component-scoped?](https://stackoverflow.com/questions/47090574/how-to-make-react-css-import-component-scoped) – jmargolisvt Jul 25 '20 at 20:37
  • I understand that CSS modules will simply do the job of wrapping my CSS in a parent tag for me. My question is really about why code splitting does not take care of this? I thought the whole point of code splitting is that JS/CSS wasn't loaded until it was needed. – MSOACC Jul 25 '20 at 20:41

2 Answers2

7

If you want to localize CSS rules, then you would have to switch to modular stylesheets (works the same for sass stylesheets).

In your current structure, the component imports non-modular stylesheet and doesn't localize the changes with a unique identifier. Therfore added rules live in a global scope without a unique identifier that would localize them so that only selected components could understand them. That means that they are capable of easily overwriting the same-named rules which were previously established (import order matters here, because it would dictate how the bundler appends the output stylesheet).

So instead of holding component-related rules within ./style.scss file, rename it to ./index.module.scss and then you would utilize it within the component like so:

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

const HomePage = () => {
    return (
        <div className={style.homepage}>
            <h1 className={style.heading}>Landing page</h1>
        </div>
    );
};

export default HomePage;

and your stylesheet would look like:

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

disclaimer:

I've changed the styling convention from selecting elements by their tag, to selecting them by class, because targetting elements by tag is widely considered a bad practice [ref] , but if you want to maintain it, then you would have to provide a parent scope for such a rule (it already exists since the parent <div/> element has an assigned class. In this case the implementation would look like:

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

const HomePage = () => {
    return (
        <div className={style.homepage}>
            <h1>Landing page</h1>
        </div>
    );
};

export default HomePage;

and styles:

.homepage {
    h1 {
        color: #f3f3f3;
        font-family: "Cambria";
        font-weight: normal;
        font-size: 2rem;
    }
}
Wiktor Bednarz
  • 701
  • 3
  • 7
  • 1
    Thanks, your second solution is what I have done. I am actually modifying the body tag in this CSS file too so I didn't want to go down the CSS modules route. I guess this just changes my understanding of code-splitting. So am I right in thinking that all CSS for all React components gets compiled into one big CSS bundle loaded on first load? If that's true then I really have no idea how code-splitting works. – MSOACC Jul 25 '20 at 20:49
  • 1
    That's pretty much correct (there might be deviations however, so don't cite me on this and treat it as a rule of thumb). Modules just prepend the rules with unique identifiers. But as stated within the answer, I think that the root of your problem is providing styles by tag type selectors - best to avoid it. – Wiktor Bednarz Jul 25 '20 at 20:53
  • Just reading here and it looks like React does NOT implement code splitting by default - even flavours like Next.js and Gatsby - and that if you want to achieve what I thought was default behaviour you have to implement lazy loading through React.lazy(). Good to know! I will update my question to include this info. Thanks for your help :) – MSOACC Jul 25 '20 at 20:55
3

You can either go with the easiest way that is mentioned below.

import React from 'react';
import './style.scss';

const HomePage = () => {
    return (
    <div className = "home">
            <div className="homepage">
                <h1>Landing page</h1>
            </div>
    </div>
    );
};

export default HomePage;

You can wrap whole html inside one div of particular component name

CSS:

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

This is the easiest way. However this is my personal solution because I also face the same issues when I was beginner at react.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Krenil
  • 96
  • 7