Carbon: Inefficient imports in carbon-components-react

Created on 10 Dec 2019  ·  16Comments  ·  Source: carbon-design-system/carbon

What package(s) are you using?

  • [x] carbon-components-react

Detailed description

I don't even know how to describe it. Right now the whole library is vastly bloated and inefficient. Let's say I want to import dropdown component, here's what "import cost" plugin tells me:

dropdown

Wow, a whooping 1,49 MB just to import dropdown component? Okay, going further. Inside the dropdown component:

settings

As I can see, settings is used only for adding prefixes. Why can't you use this import:

settings1

Ahhh, so much better! Already saved 205 KB. Okay, coming next.

icons

Is it really necessary to pull the whole icons-react library? Why don't you use this instead:

icons1

Saved 1,3 MB!

And so on, you get the idea. Same happens in every single component. Can you fix this to make the library at least somewhat usable?

question ❓

Most helpful comment

@joshblack,

I am having a similar issue where carbon/icons-react appears to pull in the whole library (1.8mb) despite specific icons being included.

import { Reset16, PlayFilledAlt16, PauseFilled16 } from '@carbon/icons-react';

I have the snippet above in two components within my code.

Is this expected behavior?

image

Update:

I also had used the UIShell component at some point in my code.
import { Header } from 'carbon-components-react/lib/components/UIShell';
Removing this UIShell code, indeed removed the large '@carbon/icons-react' import. @joshblack I suspect the UIShell is somehow pulling in the entire '@carbon/icons-react' - this might be something worth looking in to.

-V

Same

All 16 comments

Hi @r007! 👋 Thanks so much for taking the time to make this issue 😄

One quick note, the hints that you are getting from your editor are seemingly not taking tree-shaking into account. I believe if you were looking at a bundle analyzer through webpack or an equivalent bundler, you would notice that the size would be similar to a direct import because unused exports are dropped.

There are definitely places where we need to improve on bundle size, but just wanted to point out that these hints aren't necessarily accurate for one's usage of the system if using a bundler like webpack 👍

On a related note, if using React components this is also an issue with properties on classes being considered a side-effect and therefore are not correctly stripped by dead-code elimination algorithms. For this specific issue, we definitely hear and plan to address it 👍

Hi @joshblack, no problem. Bundle analyzer is showing similar results, I tested on production build as well. Here's the screenshot from production build:

webpack

  • Icons-react. Stat size: 1.7 MB, Parsed size: 1.39 MB, Gzipped size: 111.25 KB.
  • Carbon-components. Stat size: 415.51 KB, Parsed size: 156.03 KB, Gzipped size: 27.05 KB.

As you can see, carbon-components modules are the biggest modules in our app. I literally added just one dropdown on this page https://ocius.com.au/live/. And to demonstrate you that dead code was not eliminated by tree-shaking/UglifyJS plugin, take a look at this screenshot:

webpack1

...and now I deleted dropdown:

dropdown1

Images speak for themselves. Even after all tree-shaking, minification and stuff, we still have an elephant sitting in the room. Here's the bundle analyzer image after deleting dropdown:

dropdown_removed

Still big enough, but much better. So, does it sound accurate for you?

@r007 I think it's important that we distinguish between it not tree-shaking for a given setup versus it being unable to be tree-shaken/DCE'd. From a stock create-react-app install, we would get the following sizes reported when using Dropdown:

File sizes after gzip:

  69.92 KB             build/static/js/2.f1c80206.chunk.js
  768 B                build/static/js/runtime-main.97a5a035.js
  419 B (-5 B)         build/static/js/main.c17ed32b.chunk.js
  269 B                build/static/css/main.5ecd60fb.chunk.css

image

Definitely shows how much we need to improve for carbon-components and flatpickr, for sure, but it does seem like there is a configuration issue in the setup that is preventing things from being dead-code eliminated.

@joshblack indeed, flatpickr here is not really needed. We are not using create-react-app, we use gatsby.js instead (quite a popular static website generator, which allows to host websites on CDN cheaply). And from a stock Gatsby.js install, it works like I mentioned above. The source code of our website is available at Github, feel free to check.

I haven't checked next.js/create-react-app/razzle/after.js, so I'm talking purely from gatsby standpoint. Not sure how it works under the hood. But here are some related discussions:
https://github.com/gatsbyjs/gatsby/issues/7581

I need to dig deeper into it...

Nice! We love Gatsby and use it for our website properties. When building with this stock template: https://www.gatsbyjs.org/starters/alxshelepenok/gatsby-starter-lumen/ we seem to be getting something like the following in common:

image

Similar issue as with above with flatpickr, but it does seem like carbon-components, carbon-components-react, and @carbon/icons-react are being DCE'd.

Commons-*.js is a wrong bundle. Correct file to look is a "component--src-pages-name-of-the-page-....js".

Plus you're using highly opinionated starter. Here's what I did:

$ gatsby new carbon-test
info Creating new site from git: https://github.com/gatsbyjs/gatsby-starter-default.git
Cloning into «carbon-test»…
$ cd carbon-test
$ npm install --save carbon-components-react carbon-components

Then, I changed the content of src/pages/index.js:

import React from "react"
import { Link } from "gatsby"

import Dropdown from 'carbon-components-react/lib/components/Dropdown'

import Layout from "../components/layout"
import Image from "../components/image"
import SEO from "../components/seo"

const IndexPage = () => (
  <Layout>
    <SEO title="Home" />
    <h1>Hi people</h1>
    <p>Welcome to your new Gatsby site.</p>
    <p>Now go build something great.</p>
    <Dropdown
      id="test"
      type="default"
      label="Just testing"
      ariaLabel="Dropdown"
      titleText="Title:"
      items={['One', 'Two', 'Three']}
      selectedItem={'One'}
    />
    <div style={{ maxWidth: `300px`, marginBottom: `1.45rem` }}>
      <Image />
    </div>
    <Link to="/page-2/">Go to page 2</Link>
  </Layout>
)

export default IndexPage

Result is the same. Still 1.7 megabytes.

szoter_annotated_image(9)

@r007 I promise I'm not intentionally using a specific or highly opinionated starter. I just used the first one that came up when searching for a starter 😄

Most likely what is causing the issue is this line:

import Dropdown from 'carbon-components-react/lib/components/Dropdown'

The lib directory exports a CommonJS module instead of an ESM one. Here are the steps that I went through:

npx gatsby new tmp https://github.com/gatsbyjs/gatsby-starter-default.git
cd tmp
yarn add carbon-components carbon-components-react carbon-icons

I then edit src/pages/index.js:

// src/pages/index.js
import React from "react"
import { Link } from "gatsby"
import { Dropdown } from "carbon-components-react"

import Layout from "../components/layout"
import Image from "../components/image"
import SEO from "../components/seo"

const IndexPage = () => (
  <Layout>
    <SEO title="Home" />
    <h1>Hi people</h1>
    <p>Welcome to your new Gatsby site.</p>
    <p>Now go build something great.</p>
    <div style={{ maxWidth: `300px`, marginBottom: `1.45rem` }}>
      <Image />
    </div>
    <Link to="/page-2/">Go to page 2</Link>
    <Dropdown
      id="test"
      type="default"
      label="Just testing"
      ariaLabel="Dropdown"
      titleText="Title:"
      items={["One", "Two", "Three"]}
      selectedItem={"One"}
    />
  </Layout>
)

export default IndexPage

And then run:

yarn build

And get the following result for component--src-pages-index-js-<hash>

image

Specifically:

  • carbon-components-react at 13.41KB
  • @carbon/icons-react at 4.33KB
  • carbon-components at 57.8KB

@joshblack hmmm, interesting. Actually it did the trick, awesome thanks. But can we shrink down the bundle size even futher? Flatpickr is not used anyhow (47 kb), also carbon-components with all these floating menus, overflow menus are not used. Can eliminate the dead code?

Was surprised by that, as well 🤔 made a couple of issues to investigate:

https://github.com/carbon-design-system/carbon/issues/4872

https://github.com/carbon-design-system/carbon/issues/4881

Going to close this one in preference of those other ones, will definitely figure out what's going on!

Okay, thanks!

@joshblack,

I am having a similar issue where carbon/icons-react appears to pull in the whole library (1.8mb) despite specific icons being included.

import { Reset16, PlayFilledAlt16, PauseFilled16 } from '@carbon/icons-react';

I have the snippet above in two components within my code.

Is this expected behavior?

image

Update:

I also had used the UIShell component at some point in my code.
import { Header } from 'carbon-components-react/lib/components/UIShell';
Removing this UIShell code, indeed removed the large '@carbon/icons-react' import. @joshblack I suspect the UIShell is somehow pulling in the entire '@carbon/icons-react' - this might be something worth looking in to.

-V

Hi @victordibia! 👋 This definitely isn't expected for it to pull in the full bundle, most likely it's related to using the lib entrypoint as this gives exports in CommonJS, and in turn this will use the CommonJS exports from @carbon/icons-react. Updating to import directly from the entrypoint, like import { Header } from 'carbon-components-react'; should help in latest versions, or changing lib to es 👍 This will point to the ESM exports which will be tree-shaken / dead-code eliminated.

Hi @joshblack thanks for the hint, also solved a similar problem for our team. I think it would be great to update the documentation e.g. https://www.carbondesignsystem.com/tutorial/react/step-1/ to use also ESM exports
image or at least to leave a note for this.

@joshblack,

I am having a similar issue where carbon/icons-react appears to pull in the whole library (1.8mb) despite specific icons being included.

import { Reset16, PlayFilledAlt16, PauseFilled16 } from '@carbon/icons-react';

I have the snippet above in two components within my code.

Is this expected behavior?

image

Update:

I also had used the UIShell component at some point in my code.
import { Header } from 'carbon-components-react/lib/components/UIShell';
Removing this UIShell code, indeed removed the large '@carbon/icons-react' import. @joshblack I suspect the UIShell is somehow pulling in the entire '@carbon/icons-react' - this might be something worth looking in to.

-V

Same

Was this page helpful?
0 / 5 - 0 ratings

Related issues

kalyanixraut picture kalyanixraut  ·  3Comments

ConradSchmidt picture ConradSchmidt  ·  3Comments

PatrickDuncan picture PatrickDuncan  ·  3Comments

laurenmrice picture laurenmrice  ·  3Comments

ahoyahoy picture ahoyahoy  ·  3Comments