question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

`makeStyles` API creates thousands of `<style>` tags synchronously

See original GitHub issue

We use hooks with dynamic props extensively. I might even say it completely got out of hand, our codebase basically has hooks for every CSS property you can set, for example:

    const { widthClass } = useWidth({ width })
    const { fullWidthClass } = useFullWidth({ fullWidth })
    const { maxWidthClass } = useMaxWidth({ maxWidth })
    const { flexChildClass } = useFlexChild({ flexGrow, flexShrink, alignSelf })
    const { spacingClass } = useSpacing(props)
    const { verticalSpacingClass } = useVerticalSpacing({ verticalSpacing })
    const { horizontalSpacingClass } = useHorizontalSpacing({ horizontalSpacing })
    const { overflowClass } = useOverflow({ overflow })

However, this extensive usage is super bad for performance, as it seems the hooks API creates and injects 1) empty styles 2) synchronously.

The profile starts here: https://github.com/mui-org/material-ui/blob/d4c7c0586bb7853705ed1337f3f8392eb892a1bc/packages/material-ui-styles/src/makeStyles/makeStyles.js#L225 and ends up here: https://github.com/cssinjs/jss/blob/0b17c5d9717141565e466ea0dafdf3ccbe86df5a/packages/jss/src/DomRenderer.js#L335

However, up to 99% of the <style> tags end up empty:

> [...document.querySelectorAll('[data-jss][data-meta="makeStyles"]')].length
1227
> [...document.querySelectorAll('[data-jss][data-meta="makeStyles"]')].filter(e => e.innerHTML.trim() === '').length
1215
> 1215 / 1227
0.9902200488997555
  • This is not a v0.x issue.
  • I have searched the issues of this repository and believe that this is not a duplicate.

Possibly related to https://github.com/mui-org/material-ui/issues/16111 maybe?

Expected Behavior 🤔

  • jss should not create empty style tags
  • jss should group DOM updates

Current Behavior 😯

  • 99% of the <style> tags created by jss are empty
  • the style tags are inserted inside useSynchronousEffect and do DOM thrashing

Steps to Reproduce 🕹

Link: https://www.eversports.style/design/icons

Just loading the page takes ages, navigating between Typography and Icons sections takes in the order of 2-3 seconds, on a beefy desktop browser.

sorry for not providing a reduced testcase, but meh, its quite clear whats going on.

Context 🔦

Your Environment 🌎

Tech Version
Material-UI v4.2.1
React v16.8.6
jss v10.0.0-alpha.17

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:22
  • Comments:10 (4 by maintainers)

github_iconTop GitHub Comments

6reactions
Swatinemcommented, Sep 7, 2019

@wereHamster oh nice, I didn’t know…

Then it becomes:

Array.from(document.styleSheets).map(s => s.rules.length).length
1266
Array.from(document.styleSheets).map(s => s.rules.length).filter(l => l > 0).length
1249
Array.from(document.styleSheets).map(s => s.rules.length).filter(l => l > 1).length
48

So most of these are single rule stylesheets.

[...document.styleSheets].flatMap(s => [...s.cssRules]).length
1416
new Set([...document.styleSheets].flatMap(s => [...s.cssRules]).map(r => r.cssText)).size
1347

Most of the rules however are unique. That kind of surprises me… But actually looking at some of the rules makes me facepalm even more…

[...document.styleSheets].flatMap(s => [...s.cssRules]).map(r => r.cssText)
​​1087: ".c61 { font-weight: normal; }"
​​1088: ".c91 { font-weight: normal; }"
… this goes on for hundreds of rules…
​​​​1138: ".c94 { }"
​​1139: ".c107 { }"
… hundreds of these as well…
​​​201: ".c103.c103 > * { margin-right: 8px; }"
​​​202: ".c103.c103 > :last-child { margin-right: 0px; }"
… hm, some specificity hacks maybe?

So there is no deduplication going on …

Lets investigate further…

new Set([...document.styleSheets].flatMap(s => [...s.cssRules]).map(r => r.cssText).filter(t => t.startsWith('.c')).map(t => t.replace(/^\.c\d+/, ''))).size
84
new Set([...document.styleSheets].flatMap(s => [...s.cssRules]).map(r => r.cssText).filter(t => t.startsWith('.c')).map(t => t.replace(/^(\.c\d+)+/, ''))).size
66

So we are down to < 100 unique classname rules, even less when we remove duplicates due to specificity…

Well not only do we have all these mostly duplicate rules injected into the head, they also have uselessly unique classnames, which defeats the whole purpose style sharing via classes, and blows up the generated html as well…

6reactions
wereHamstercommented, Sep 6, 2019

@Swatinem I don’t think these style tags are actually empty. When you create a <style> element and add the rules to it via JS API (which many of the CSS-in-JS solutions do, due to performance reasons), then the resulting CSS won’t show up as the innerHTML string. You should use document.styleSheets to enumerate all stylesheets and then check .rules.length to see if they are really empty.

Read more comments on GitHub >

github_iconTop Results From Across the Web

`makeStyles` API creates thousands of `<style>` tags ...
makeStyles API creates thousands of <style > tags synchronously #16756 ... hooks API creates and injects 1) empty styles 2) synchronously.
Read more >
Material UI makeStyles() and withStyles() added too many ...
I recently started using Material UI library for my react projects and i found it super useful until i noticed those <style> tags...
Read more >
Material-Ui Creates Extra ≪Style Data-Jss="" Data-Meta=". ...
Each UI component has an API described in the UI component's API reference ... makeStyles API creates thousands of <style> tags synchronously #16756....
Read more >
Styles API - MUI System
The API reference of @mui/styles. ... Its only purpose is to defeat TypeScript's type widening when providing style rules to makeStyles / withStyles...
Read more >
React Basics - GitHub Pages
React is a JavaScript library created for building fast and interactive user interfaces for ... In JSX, lower-case tag names are considered to...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Hashnode Post

No results found