Gatsby: Global CSS imported on one page gets imported on all pages

Created on 26 May 2019  路  16Comments  路  Source: gatsbyjs/gatsby

Description

Hi, I am migrating and combining some older sites into one gatsby site. I need to import global CSS on some pages, but not on others. The issue is that if I import the CSS on one page, it gets included in all the other pages.

I'm importing the CSS from the specific page that needs it, so I would expect it to show only on that page. Is this a bug?

I've seen this issue but the suggested solution there is to use css modules, and migrating to CSS modules would be very time consuming at this point (initial migration phase).

I've also tried the solution here but this inlines the CSS, whereas I'd like to have it as a separate file.

Steps to reproduce

  1. Create a css file styles.css
  2. Import css file into one page import '../styles.css
  3. Open a different page and see that styles.css is included there too.

Edit: I created a repo to demonstrate the issue here: https://github.com/amakk/gatsby-global-css-bug

index.js imports style.css while page2.js doesn't import it. Yet, both get the style from style.css applied.

Expected result

The CSS imported on one page should only be included into that page, not on other pages.

Actual result

All pages include the CSS.

Environment

System:
OS: macOS 10.14
CPU: (8) x64 Intel(R) Core(TM) i7-4850HQ CPU @ 2.30GHz
Shell: 3.2.57 - /bin/bash
Binaries:
Node: 8.11.3 - /usr/local/bin/node
Yarn: 1.16.0 - /usr/local/bin/yarn
npm: 6.4.1 - /usr/local/bin/npm
Languages:
Python: 2.7.10 - /usr/bin/python
Browsers:
Chrome: 74.0.3729.169
Firefox: 66.0.3
Safari: 12.0
npmPackages:
gatsby: ^2.0.65 => 2.5.5
gatsby-image: ^2.0.23 => 2.1.0
gatsby-plugin-google-tagmanager: ^2.0.7 => 2.0.13
gatsby-plugin-manifest: ^2.0.11 => 2.1.1
gatsby-plugin-offline: ^2.0.19 => 2.1.1
gatsby-plugin-postcss: ^2.0.2 => 2.0.7
gatsby-plugin-react-helmet: ^3.0.4 => 3.0.12
gatsby-plugin-sass: ^2.0.11 => 2.0.11
gatsby-plugin-sharp: ^2.0.15 => 2.0.37
gatsby-plugin-styled-components: ^3.0.4 => 3.0.7
gatsby-plugin-webpack-bundle-analyzer: ^1.0.3 => 1.0.5
gatsby-source-filesystem: ^2.0.12 => 2.0.36
gatsby-transformer-sharp: ^2.1.9 => 2.1.19
npmGlobalPackages:
gatsby-cli: 2.5.15

Most helpful comment

Facing the same issue for multiple layouts using scss.
I have two scss files style1.scss and style2.scss and I have imported style1.scss in layout1.js and style2.scss in layout2.js. But in the DOM for any layout both the scss files are overriding.

All 16 comments

Hi!

Sorry to hear you're running into an issue. To help us best begin debugging the underlying cause, it is incredibly helpful if you're able to create a minimal reproduction. This is a simplified example of the issue that makes it clear and obvious what the issue is and how we can begin to debug it.

If you're up for it, we'd very much appreciate if you could provide a minimal reproduction and we'll be able to take another look.

Thanks for using Gatsby! 馃挏

Hi @LekoArts, I created a repository to reproduce the issue here: https://github.com/amakk/gatsby-global-css-bug.

index.js imports style.css while page2.js doesn't import it. Yet, both get the style from style.css applied.

Thanks for your help!

CSS is global by nature. You can fix it by wrapping it with a unique class (.someclass h1), the problem is... It is too long. That is why I love sass, they can do nesting.

A possible solution is to use CSS Modules by installing PostCSS. After installing, you will have to append .module.css to the file name. (index.css => index.module.css) and import it as usual.

Facing the same issue for multiple layouts using scss.
I have two scss files style1.scss and style2.scss and I have imported style1.scss in layout1.js and style2.scss in layout2.js. But in the DOM for any layout both the scss files are overriding.

Thanks for the suggestions @vinzun and @ImedAdel. Unfortunately, these wouldn't work in my case, mainly for two reasons:

  1. When importing something like bootstrap CSS, it modifies the html element, and probably others. Not sure if it's even possible to wrap all that with a class but even if it's possible, there is another issue
  2. The size of the CSS will really increase, since I'm combining several sub-sites into one, so I wouldn't want to load the equivalent of 3 sites worth of CSS on each page.

What I'm really after is an equivalent to including an external stylesheet via a link tag (which is how it's done in the current website) that I could add/remove on specific pages. I know I could just put the CSS in a static folder and create a link tag, but some of it is written in SASS and I would also lose out on cache busting.

Any other suggestions?

oh I see. Never used bootstrap before, sorry I can't help you.

Does anyone else have other ideas? Here's what I'm doing for now:

import bootstrap from '!raw-loader!bootstrap/dist/css/bootstrap.min.css'

And then putting it into a style tag:

<Helmet>
        <style>{bootstrap}</style>
</Helmet>

But I'm wondering how efficient this is. If I put this in a layout component, will it get re-downloaded on each page navigation? Also, there isn't a way to tell which CSS file the styles are coming from in Chrome inspector.

@LekoArts could you update the status labels of this issue since I've added all needed info and reproduction

@amakk I can't say whether your solution is the best approach, but it's not downloading it on each page navigation, it will get it once as part of the initial JS bundle download and then use that version for subsequent page loads.

Using raw-loader copies the entire contents of the file to a string at build time and embeds it into the final JS output, so that import call is equivalent to

// component.js
import bootstrap from './bootstrap.min.css.js';

// bootstrap.min.css.js
export default `
  .class1 { /* ... */ }
  .class2 { /* ... */ }
  // ...
`;

@superhawk610 Thanks, that's good to know. Would appreciate any tips on a better approach.

For smaller scale styles you can make use of CSS Modules as suggested earlier. They'll only be loaded on pages where they're required - you can find more information on using them with Gatsby here.

For your use case, I would recommend writing a separate layout component for each nested site that requires its own stylesheets, then inject the stylesheets using react-helmet and <link rel="stylesheet" /> tags. This way, each stylesheet will only be loaded when the page layout requires it!

If you'd like to discuss further, feel free to open a thread on Spectrum and link to this issue. Best of luck!

@superhawk610 Question about this: Is there a way to do this with gatsby-plugin-sass? It seems to store the stylesheet with random names so not sure how to use that here

In my case, I am abstracting code into different npm packages and each one of them had a global imported css file

In the main gatsby website, each package was used on different pages, but all global css files were downloaded/injected in all pages (also in those not using the packages at all)

I'm now deciding how to avoid using global css files in the packages, but since I've found this issue I comment this for future readers

@amakk did you manage to find a solution? I am having a similar problem trying to generate sites based on different templates but using the same repository, the CSS problem is the only sho stopper.

Appreciate your feedback!

I've ended up with a bunch of updates to tackle the issue of having isolated templates, is not the best approach I am sure, but I'll share what I have in case it helps somebody:

  1. I am using CSS modules, so I've added a main.module.css file which contains my global classes and custom properties, something like this:
.theme {
    --color-main: red;
    .title {
        color: var(--color-red);
    }
}
  1. I am passing that wrapper class to react-helmet and assign it to the body element
import main from main.module.css

<Helmet
     bodyAttributes={{ class:  main.theme }}
/>
  1. And finally to avoid load the fonts of each template in all the pages, I've created separated CSS font loader files in the static/ gatsby folder. I am loading these fonts using React Helmet as well

    ```
    import main from main.module.css
    const fontsPath = 'theme-fonts-folder'

/fonts/${fontsPath}/index.css} />

```

Was this page helpful?
0 / 5 - 0 ratings

Related issues

rossPatton picture rossPatton  路  3Comments

ferMartz picture ferMartz  路  3Comments

brandonmp picture brandonmp  路  3Comments

ghost picture ghost  路  3Comments

signalwerk picture signalwerk  路  3Comments