Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should global css be put in each component or the root component (webpack entry file)?

I am wondering that if I have an external CSS file which is frequently used in my components, should I import this external CSS inside each component or the root component?

For each component:

import React from 'react'
import '../font.css'

class MyComponent extends React.Component {
  render() {
    return <div className="fa fa-bandcamp"></div>;
  }
}

This is self-explanatory: because I want to use 'fa fa-bandcamp', I import '../font.css'.

This methodology is just like programming JS or any other programming languages. If we need a dependency, we import it in that file as well, for example:

import global from 'global'
import util from 'util'

global.foo
global.bar
util.bar
util.bar
// ...

However, my colleague told me that global css should never be imported inside every depending components, instead, it should be imported inside a root component or in the entry file of webpack, for example:

// in each component
import React from 'react'
// import '../font.css'

class MyComponent extends React.Component {
  render() {
    return <div className="fa fa-bandcamp"></div>;
  }
}

// in entry file (root component)
import React from 'react'
import '../font.css'

class App extends React.Component {
  render() {
    return <div>{this.props.children}</div>;
  }
}

What's the pros and cons of each solution? I would like to hear more advices and appreciate your help.

like image 496
Weihang Jian Avatar asked Aug 08 '17 07:08

Weihang Jian


2 Answers

I'd import your font.css file when and where you use it (but not exactly the way you suggest, see below) and not just in the root component. I suggest this because when and if you decide to code split, you would only want that CSS to exist in the bundle that uses it.

If the import is in your root component, you might remove all components that are using the fa fa-bandcamp classes but your import remains in the root (because you forget it's there and not alongside your component) and you'd be bundling in CSS that is not even in the chunk that uses it.

On the contrary though, when importing at the component level you could also end up in a situation where you use those classes and forget to import that font.css because ANOTHER component has imported the global CSS already. It looks like it works but if you code split you might find that your chunk does not have the right font because the CSS import is in another chunk. In this case importing it in the root would have solved your issue!

What I would do:

I would argue that any global css is bad and you should be using something like CSS modules. So I'd go one step further and create a <Text/> component that is something like:

import React from 'react'
import '../font.css'
export default ({ className, children, tagName: TagName }) => <TagName className={`fa fa-bandcamp ${className}`>{ children }</TagName>;

Now you can use the <Text tagName="span">Hey!</Text> in all your components safely because:

  • You no longer have to import the CSS all the time.
  • If you code split, or remove all <Text/> you won't be left with a bundle that contains unused CSS imports in the root that you forgot about.
  • It's not possible to use those classes and forget to import the CSS.
  • Everything is nice and encapsulated in a modular way and your bundles are as efficient as possible.

I wouldn't employ this kind of strategy for something like a reset.css though. Obviously.

TL;DR Summary

Root level - Potential inefficient code splitting. Harder to maintain as CSS does not live along side the component that uses it.

Individual component level - Pain to import all the time. Fragile as you can end up using a class that doesn't exist in a chunk if forgetting to import the global CSS.

"Text" component - Awesome. Just make sure everybody uses the fa classes via this component and everything is golden. Modular. Easy to maintain. Robust.

like image 112
Matt Derrick Avatar answered Oct 13 '22 20:10

Matt Derrick


They are almost right when your colleagues say:

that global css should never be imported inside every depending components, instead, it should be imported inside a root component or in the entry file of webpack

Why?

Because then you might end up having inline css files, giving an example, in one of my application, where I used less, every import resolved into a local copy of the same inline into javascript file, the generated bundle.js file in my case, Snippet:


, /* 1381 */
/***/
function(module, exports, __webpack_require__) {

    eval("exports = module.exports = __webpack_require__(625)();\n// imports\nexports.push([
module.id, \"@import url(https://fonts.googleapis.com/css?family=Open+Sans:400,600,700);\", \"\"]);
\n\n// module\nexports.push([module.id, \"/*!\\n * Bootstrap v3.3.7 (http://getbootstrap.com)\\n * 
Copyright 2011-2016 Twitter, Inc.\\n * Licensed under MIT 
(https://github.com/twbs/bootstrap/blob/master/LICENSE)\\n */\\n/*! normalize.css v3.0.3 | MIT License |
 github.com/necolas/normalize.css */\\nhtml 
{\\n  font-family: sans-serif;\\n  -ms-text-size-adjust: 100%;
\\n  -webkit-text-size-adjust: 100%;\\n}\\nbody 
{\\n  margin: 0;\\n}\\narticle,\\naside,\\ndetails,\\nfigcaption,
\\nfigure,\\nfooter,\\nheader,\\nhgroup,\\nmain,\\nmenu,\\nnav,\\nsection,
\\nsummary {\\n  display: block;\\n}\\naudio,\\ncanvas,\\nprogress,
\\nvideo {\\n  display: inline-block;\\n  vertical-align: baseline;\\n}
\\naudio:not([controls]) 
{\\n  display: none;\\n  height: 0;\\n}\\n[hidden],\\ntemplate {\\n  display: none;\\n}\\na {\\n  background-color: transparent;\\n}

   Some more things here...

    /***/
}

See the bootstrap css license at the start? But then it also uses @import url for fonts css which was imported in the file.

So every time you import, it would be included in the module code again and again.

Things to use

If you use something like Extract Text Webpcak Plugin, which basically scans your code and extracts the css into a file which also helps in parallel load. Thus solving your same css file import multiple times solution.

Observation:

In my case my webpack uses a less styling and hence my loader is something like:

Webpack v1.13.*:

  {
     test: /\.less$/,
     loader: 'style!css!less'
  }

Which in my case is adding it not under a style tag, but embedded inside the javascript function.

In your case, you are using a style-loader which by definition is:

style-loader - Injects the CSS, that is exported by the JavaScript module, into a tag at runtime

Now, if you verify, by importing the same global.css in your case, a single <style> is created which would be the one time definition.

Showing your bundle.js pushes the same, when you generate it:

/***/ }),
/* 2 */
/***/ (function(module, exports, __webpack_require__) {

exports = module.exports = __webpack_require__(3)(undefined);
// imports


// module
exports.push([module.i, ".red {\n  color: red;\n}\n\n.blue {\n  color: blue;\n}\n", ""]);

// exports


/***/ }),

That is why in your index.html you will see the <style> tag added by bundel.js:

function insertStyleElement (options, style) {
   ....
}

Conclusion:

  • Global importing the styles

    pros: is a good practice in case you have base css like bootstrap.css and font-awesome, because it is a lot neater to know, that your entire application adheres to that rule of css.

    cons: None. In case the css are common, then I don't see any cons of importing it globally

  • Local importing of styles

    pros: It will always be lot easier to debug in case you know which css is affecting the component when you want to drill down to any bug. These css files are the ones that you would be implementing.

    cons: There are a very few being, in case there was a bug of border issue, and while debugging, you realized that it points to a css file imported locally, but you failed to realize that it affects other components then it would be causing an unwanted change in your UI. Also it would be lot slower for the styles to be extracted during build time.

like image 42
Nagaraj Tantri Avatar answered Oct 13 '22 19:10

Nagaraj Tantri