Repository: mui/material-ui (TypeScript)
Date: 2018-10-13 21:41:06+00:00
Task type: Feature

Issue:
[Discussion] Benchmark components
I've done a benchmark page between `material-ui` and `react-toolbox` because I wanted evaluated the alternatives that might have better performance. I will be happy if you guys could do some PRs with more benchmarks. I think you can get some benefit by doing this because you can see who performant your components are and also how good they are compared to the other material design frameworks and if you score well it might be good for marketing.

https://github.com/EloB/benchmark-material

I been having some problems with performance especially from lists (thats why I made my repo is the first place). I've discovered that passing "bad props" like passing new instances have really bad impact on the performance. This is not really good documented on your site or the on React site but it's more or less a React issue.

Also that having a iterator component could improve the performance alot! If you can include that somehow in your repo that could be really good.



Issue discussion:
@EloB We're working on big performance improvements for `0.16.0`. We're tackling styles first as that is a global perf issue, and then going to look component-by-component at specifics caused by other issues.

It's unlikely that we're going to want to make a big song and dance about benchmark results until the big known issues are resolved.

I also made [a little benchmark component](https://github.com/nathanmarks/material-ui/blob/textfield-performance/test/browser/fixtures/perf/TimeWaster.jsx) a while back to check wasted time in re-renders. I intend on doing more in that area once they release `15.1.0` with the react-perf revamp: https://github.com/facebook/react/pull/6046

The best way to incorporate benchmark components here would be to add a benchmarking section to the test suite which compares against some control values.

@nathanmarks I seen that you guys do `mergeStyle({ some: 'css', properties: 'here' }, this.props.style)` and inside that method it does `Object.assign({}, ...args)`. A easy win could be to just return the first argument if you don't have any `this.props.style` or the `args` is an empty array. So you don't create a new object all the time that doesn't pass a `shallowEqual`.

I've built 4 projects where I heavily use material-ui and because of performance issues during each project I have to remove some elements and recreate those by myself or just make some simplified version of node.
- Before i had issues that clicking on `TextInput` redraw was made for also surrounding elements, not only input.
- Right now i have issue with too many dom nodes & listeners with `Toggle` element. Removing it completely and making other UI decisions.
- I think buttons could also be made with less nodes. It's super popular component and used in tables a lot where you have rows and rows of buttons.

Haven't detected other components to have these issues, because i haven't tested those too much. I believe you should make some testing of how many nodes/listeners each component create and is it possible to reduce those numbers. Thanks for your hard work. 

Wow, I have just discovered that thread. Interesting.
I'm really surprised that Material-UI outperforms React Toolbox. It's 30 FPS when scrolling vs 15 FPS and a first paint 10% sooner.
Seriously, I don't understand what's explaining that different. I would love to see the same benchmark with the `next` branch. Would you mind giving it a shot 👼 ?

Solution patch:
diff --git a/.size-limit.js b/.size-limit.js
--- a/.size-limit.js
+++ b/.size-limit.js
@@ -22,7 +22,7 @@ module.exports = [
     name: 'The size of all the material-ui modules.',
     webpack: true,
     path: 'packages/material-ui/build/index.js',
-    limit: '92.7 KB',
+    limit: '93.2 KB',
   },
   {
     name: 'The main docs bundle',
diff --git a/packages/material-ui-benchmark/README.md b/packages/material-ui-benchmark/README.md
new file mode 100644
--- /dev/null
+++ b/packages/material-ui-benchmark/README.md
@@ -0,0 +1,14 @@
+# Material-UI Benchmarking
+
+```sh
+yarn benchmark
+
+ButtonBase x 78,875 ops/sec ±11.38% (80 runs sampled)
+Grid x 84,284 ops/sec ±2.36% (87 runs sampled)
+JssButton x 253,054 ops/sec ±4.50% (83 runs sampled)
+StyledComponents x 110,824 ops/sec ±0.79% (92 runs sampled)
+Emotion x 119,230 ops/sec ±1.81% (89 runs sampled)
+Button x 27,616 ops/sec ±3.85% (85 runs sampled)
+ButtonBase disableRipple x 85,133 ops/sec ±2.25% (84 runs sampled)
+button x 993,210 ops/sec ±4.82% (81 runs sampled)
+```
diff --git a/packages/material-ui-benchmark/package.json b/packages/material-ui-benchmark/package.json
new file mode 100644
--- /dev/null
+++ b/packages/material-ui-benchmark/package.json
@@ -0,0 +1,29 @@
+{
+  "name": "@material-ui/benchmark",
+  "private": true,
+  "version": "3.0.0",
+  "description": "Material-UI Benchmark.",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/mui-org/material-ui.git"
+  },
+  "bugs": {
+    "url": "https://github.com/mui-org/material-ui/issues"
+  },
+  "homepage": "https://github.com/mui-org/material-ui/tree/master/packages/material-ui-benchmark",
+  "scripts": {
+    "benchmark": "cd ../../ && NODE_ENV=production BABEL_ENV=docs-production babel-node packages/material-ui-benchmark/src/benchmark.js"
+  },
+  "devDependencies": {},
+  "engines": {
+    "node": ">=6.0.0"
+  },
+  "license": "MIT",
+  "dependencies": {
+    "benchmark": "^2.1.4",
+    "emotion": "^9.2.12",
+    "nodemod": "^1.5.19",
+    "react-emotion": "^9.2.12",
+    "styled-components": "^3.4.10"
+  }
+}
diff --git a/packages/material-ui-benchmark/src/benchmark.js b/packages/material-ui-benchmark/src/benchmark.js
new file mode 100644
--- /dev/null
+++ b/packages/material-ui-benchmark/src/benchmark.js
@@ -0,0 +1,98 @@
+/* eslint-disable no-console, no-underscore-dangle */
+
+import Benchmark from 'benchmark';
+import React from 'react';
+import styled from 'styled-components';
+import ReactDOMServer from 'react-dom/server';
+import styled2 from 'react-emotion';
+import Button from '@material-ui/core/Button';
+import Grid from '@material-ui/core/Grid';
+import { withStyles } from '@material-ui/core/styles';
+import ButtonBase from '@material-ui/core/ButtonBase';
+
+const suite = new Benchmark.Suite({
+  async: true,
+  minSamples: 100,
+  minTime: 5,
+});
+
+global.__MUI_USE_NEXT_TYPOGRAPHY_VARIANTS__ = true;
+
+function CustomButton() {
+  return <button type="button">Material-UI</button>;
+}
+
+const JssButton = withStyles({
+  root: {
+    display: 'inline-flex',
+    alignItems: 'center',
+    justifyContent: 'center',
+    position: 'relative',
+    // Remove grey highlight
+    WebkitTapHighlightColor: 'transparent',
+    backgroundColor: 'transparent', // Reset default value
+    // We disable the focus ring for mouse, touch and keyboard users.
+    outline: 'none',
+    border: 0,
+    margin: 0, // Remove the margin in Safari
+    borderRadius: 0,
+    padding: 0, // Remove the padding in Firefox
+    cursor: 'pointer',
+    userSelect: 'none',
+    verticalAlign: 'middle',
+    '-moz-appearance': 'none', // Reset
+    '-webkit-appearance': 'none', // Reset
+    textDecoration: 'none',
+    // So we take precedent over the style of a native <a /> element.
+    color: 'inherit',
+    '&::-moz-focus-inner': {
+      borderStyle: 'none', // Remove Firefox dotted outline.
+    },
+    '&$disabled': {
+      pointerEvents: 'none', // Disable link interactions
+      cursor: 'default',
+    },
+  },
+})(({ classes, ...other }) => <button type="button" {...other} />);
+
+const StyledComponents = styled.button`
+  font-size: 1.5em;
+  text-align: center;
+  color: palevioletred;
+`;
+
+const Emotion = styled2('button')`
+  font-size: 1.5em;
+  text-align: center;
+  color: palevioletred;
+`;
+
+suite
+  .add('ButtonBase', () => {
+    ReactDOMServer.renderToString(<ButtonBase>Material-UI</ButtonBase>);
+  })
+  .add('Grid', () => {
+    ReactDOMServer.renderToString(<Grid>Material-UI</Grid>);
+  })
+  .add('JssButton', () => {
+    ReactDOMServer.renderToString(<JssButton>Material-UI</JssButton>);
+  })
+  .add('StyledComponents', () => {
+    ReactDOMServer.renderToString(<StyledComponents>Material-UI</StyledComponents>);
+  })
+  .add('Emotion', () => {
+    ReactDOMServer.renderToString(<Emotion>Material-UI</Emotion>);
+  })
+  .add('Button', () => {
+    ReactDOMServer.renderToString(<Button>Material-UI</Button>);
+  })
+  .add('ButtonBase disableRipple', () => {
+    ReactDOMServer.renderToString(<ButtonBase disableRipple>Material-UI</ButtonBase>);
+  })
+  .add('button', () => {
+    ReactDOMServer.renderToString(<CustomButton />);
+  })
+  .on('cycle', event => {
+    console.log(String(event.target));
+  })
+  .run();
diff --git a/packages/material-ui/src/Button/Button.js b/packages/material-ui/src/Button/Button.js
--- a/packages/material-ui/src/Button/Button.js
+++ b/packages/material-ui/src/Button/Button.js
@@ -218,8 +218,8 @@ function Button(props) {
     color,
     disabled,
     disableFocusRipple,
-    fullWidth,
     focusVisibleClassName,
+    fullWidth,
     mini,
     size,
     variant,
diff --git a/packages/material-ui/src/ButtonBase/ButtonBase.js b/packages/material-ui/src/ButtonBase/ButtonBase.js
--- a/packages/material-ui/src/ButtonBase/ButtonBase.js
+++ b/packages/material-ui/src/ButtonBase/ButtonBase.js
@@ -5,6 +5,7 @@ import classNames from 'classnames';
 import keycode from 'keycode';
 import ownerWindow from '../utils/ownerWindow';
 import withStyles from '../styles/withStyles';
+import NoSsr from '../NoSsr';
 import { listenForFocusKeys, detectFocusVisible } from './focusVisible';
 import TouchRipple from './TouchRipple';
 import createRippleHandler from './createRippleHandler';
@@ -269,14 +270,13 @@ class ButtonBase extends React.Component {
       classNameProp,
     );
 
-    const buttonProps = {};
-
     let ComponentProp = component;
 
     if (ComponentProp === 'button' && other.href) {
       ComponentProp = 'a';
     }
 
+    const buttonProps = {};
     if (ComponentProp === 'button') {
       buttonProps.type = type || 'button';
       buttonProps.disabled = disabled;
@@ -286,6 +286,7 @@ class ButtonBase extends React.Component {
 
     return (
       <ComponentProp
+        className={className}
         onBlur={this.handleBlur}
         onFocus={this.handleFocus}
         onKeyDown={this.handleKeyDown}
@@ -296,15 +297,17 @@ class ButtonBase extends React.Component {
         onTouchEnd={this.handleTouchEnd}
         onTouchMove={this.handleTouchMove}
         onTouchStart={this.handleTouchStart}
-        tabIndex={disabled ? '-1' : tabIndex}
-        className={className}
         ref={buttonRef}
+        tabIndex={disabled ? '-1' : tabIndex}
         {...buttonProps}
         {...other}
       >
         {children}
         {!disableRipple && !disabled ? (
-          <TouchRipple innerRef={this.onRippleRef} center={centerRipple} {...TouchRippleProps} />
+          <NoSsr>
+            {/* TouchRipple is only needed client side, x2 boost on the server. */}
+            <TouchRipple innerRef={this.onRippleRef} center={centerRipple} {...TouchRippleProps} />
+          </NoSsr>
         ) : null}
       </ComponentProp>
     );
diff --git a/packages/material-ui/src/ButtonBase/createRippleHandler.js b/packages/material-ui/src/ButtonBase/createRippleHandler.js
--- a/packages/material-ui/src/ButtonBase/createRippleHandler.js
+++ b/packages/material-ui/src/ButtonBase/createRippleHandler.js
@@ -1,30 +1,35 @@
-function createRippleHandler(instance, eventName, action, cb) {
-  return function handleEvent(event) {
-    if (cb) {
-      cb.call(instance, event);
-    }
+/* eslint-disable import/no-mutable-exports */
 
-    let ignore = false;
+let createRippleHandler = (instance, eventName, action, cb) => event => {
+  if (cb) {
+    cb.call(instance, event);
+  }
 
-    // Ignore events that have been `event.preventDefault()` marked.
-    if (event.defaultPrevented) {
-      ignore = true;
-    }
+  let ignore = false;
 
-    if (instance.props.disableTouchRipple && eventName !== 'Blur') {
-      ignore = true;
-    }
+  // Ignore events that have been `event.preventDefault()` marked.
+  if (event.defaultPrevented) {
+    ignore = true;
+  }
 
-    if (!ignore && instance.ripple) {
-      instance.ripple[action](event);
-    }
+  if (instance.props.disableTouchRipple && eventName !== 'Blur') {
+    ignore = true;
+  }
 
-    if (typeof instance.props[`on${eventName}`] === 'function') {
-      instance.props[`on${eventName}`](event);
-    }
+  if (!ignore && instance.ripple) {
+    instance.ripple[action](event);
+  }
 
-    return true;
-  };
+  if (typeof instance.props[`on${eventName}`] === 'function') {
+    instance.props[`on${eventName}`](event);
+  }
+
+  return true;
+};
+
+/* istanbul ignore if */
+if (!process.browser) {
+  createRippleHandler = () => () => {};
 }
 
 export default createRippleHandler;
diff --git a/packages/material-ui/src/NoSsr/NoSsr.js b/packages/material-ui/src/NoSsr/NoSsr.js
--- a/packages/material-ui/src/NoSsr/NoSsr.js
+++ b/packages/material-ui/src/NoSsr/NoSsr.js
@@ -22,7 +22,7 @@ class NoSsr extends React.Component {
     this.mounted = true;
 
     if (this.props.defer) {
-      // Wondering why we use two raf? Check this video out:
+      // Wondering why we use two RAFs? Check this video out:
       // https://www.youtube.com/watch?v=cCOL7MC4Pl0
       requestAnimationFrame(() => {
         // The browser should be about to render the DOM that React commited at this point.
diff --git a/yarn.lock b/yarn.lock
--- a/yarn.lock
+++ b/yarn.lock
@@ -944,6 +944,13 @@
   resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.6.6.tgz#62266c5f0eac6941fece302abad69f2ee7e25e44"
   integrity sha512-ojhgxzUHZ7am3D2jHkMzPpsBAiB005GF5YU4ea+8DNPybMk01JJUM9V9YRlF/GE95tcOm8DxQvWA2jq19bGalQ==
 
+"@emotion/is-prop-valid@^0.6.1":
+  version "0.6.8"
+  resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.6.8.tgz#68ad02831da41213a2089d2cab4e8ac8b30cbd85"
+  integrity sha512-IMSL7ekYhmFlILXcouA6ket3vV7u9BqStlXzbKOF9HBtpUPMMlHU+bBxrLOa2NvleVwNIxeq/zL8LafLbeUXcA==
+  dependencies:
+    "@emotion/memoize" "^0.6.6"
+
 "@emotion/memoize@^0.6.1", "@emotion/memoize@^0.6.6":
   version "0.6.6"
   resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.6.6.tgz#004b98298d04c7ca3b4f50ca2035d4f60d2eed1b"
@@ -2620,6 +2627,14 @@ bcrypt-pbkdf@^1.0.0:
   dependencies:
     tweetnacl "^0.14.3"
 
+benchmark@^2.1.4:
+  version "2.1.4"
+  resolved "https://registry.yarnpkg.com/benchmark/-/benchmark-2.1.4.tgz#09f3de31c916425d498cc2ee565a0ebf3c2a5629"
+  integrity sha1-CfPeMckWQl1JjMLuVloOvzwqVik=
+  dependencies:
+    lodash "^4.17.4"
+    platform "^1.3.3"
+
 better-assert@~1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/better-assert/-/better-assert-1.0.2.tgz#40866b9e1b9e0b55b481894311e68faffaebc522"
@@ -2920,6 +2935,14 @@ buffer@^4.3.0:
     ieee754 "^1.1.4"
     isarray "^1.0.0"
 
+buffer@^5.0.3:
+  version "5.2.1"
+  resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.2.1.tgz#dd57fa0f109ac59c602479044dca7b8b3d0b71d6"
+  integrity sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==
+  dependencies:
+    base64-js "^1.0.2"
+    ieee754 "^1.1.4"
+
 buffered-spawn@~1.1.1:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/buffered-spawn/-/buffered-spawn-1.1.2.tgz#21ad9735dfbf6576745be0d74a23ef257bf3c58d"
@@ -3610,6 +3633,13 @@ create-ecdh@^4.0.0:
     bn.js "^4.1.0"
     elliptic "^6.0.0"
 
+create-emotion-styled@^9.2.8:
+  version "9.2.8"
+  resolved "https://registry.yarnpkg.com/create-emotion-styled/-/create-emotion-styled-9.2.8.tgz#c0050e768ba439609bec108600467adf2de67cc3"
+  integrity sha512-2LrNM5MREWzI5hZK+LyiBHglwE18WE3AEbBQgpHQ1+zmyLSm/dJsUZBeFAwuIMb+TjNZP0KsMZlV776ufOtFdg==
+  dependencies:
+    "@emotion/is-prop-valid" "^0.6.1"
+
 create-emotion@^9.2.12:
   version "9.2.12"
   resolved "https://registry.yarnpkg.com/create-emotion/-/create-emotion-9.2.12.tgz#0fc8e7f92c4f8bb924b0fef6781f66b1d07cb26f"
@@ -3724,6 +3754,11 @@ crypto-random-string@^1.0.0:
   resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e"
   integrity sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=
 
+css-color-keywords@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/css-color-keywords/-/css-color-keywords-1.0.0.tgz#fea2616dc676b2962686b3af8dbdbe180b244e05"
+  integrity sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU=
+
 css-loader@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-1.0.0.tgz#9f46aaa5ca41dbe31860e3b62b8e23c42916bf56"
@@ -3776,6 +3811,15 @@ css-selector-tokenizer@^0.7.0:
     fastparse "^1.1.1"
     regexpu-core "^1.0.0"
 
+css-to-react-native@^2.0.3:
+  version "2.2.2"
+  resolved "https://registry.yarnpkg.com/css-to-react-native/-/css-to-react-native-2.2.2.tgz#c077d0f7bf3e6c915a539e7325821c9dd01f9965"
+  integrity sha512-w99Fzop1FO8XKm0VpbQp3y5mnTnaS+rtCvS+ylSEOK76YXO5zoHQx/QMB1N54Cp+Ya9jB9922EHrh14ld4xmmw==
+  dependencies:
+    css-color-keywords "^1.0.0"
+    fbjs "^0.8.5"
+    postcss-value-parser "^3.3.0"
+
 css-tree@1.0.0-alpha.28:
   version "1.0.0-alpha.28"
   resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.28.tgz#8e8968190d886c9477bc8d61e96f61af3f7ffa7f"
@@ -4397,7 +4441,7 @@ emojis-list@^2.0.0:
   resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389"
   integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k=
 
-emotion@^9.1.2:
+emotion@^9.1.2, emotion@^9.2.12:
   version "9.2.12"
   resolved "https://registry.yarnpkg.com/emotion/-/emotion-9.2.12.tgz#53925aaa005614e65c6e43db8243c843574d1ea9"
   integrity sha512-hcx7jppaI8VoXxIWEhxpDW7I+B4kq9RNzQLmsrF6LY8BGKqe2N+gFAQr0EfuFucFlPs2A9HM4+xNj4NeqEWIOQ==
@@ -5131,7 +5175,7 @@ fastparse@^1.1.1:
   resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.1.tgz#d1e2643b38a94d7583b479060e6c4affc94071f8"
   integrity sha1-0eJkOzipTXWDtHkGDmxK/8lAcfg=
 
-fbjs@^0.8.1, fbjs@^0.8.4:
+fbjs@^0.8.1, fbjs@^0.8.16, fbjs@^0.8.4, fbjs@^0.8.5:
   version "0.8.17"
   resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd"
   integrity sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=
@@ -5795,6 +5839,11 @@ has-cors@1.1.0:
   resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39"
   integrity sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=
 
+has-flag@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa"
+  integrity sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=
+
 has-flag@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
@@ -8032,6 +8081,11 @@ node-releases@^1.0.0-alpha.12:
   dependencies:
     semver "^5.3.0"
 
+nodemod@^1.5.19:
+  version "1.5.19"
+  resolved "https://registry.yarnpkg.com/nodemod/-/nodemod-1.5.19.tgz#eb9a1866bb7250ea3f804e7715661aa7559800e1"
+  integrity sha1-65oYZrtyUOo/gE53FWYap1WYAOE=
+
 nomnom@^1.5.x, nomnom@^1.8.1:
   version "1.8.1"
   resolved "https://registry.yarnpkg.com/nomnom/-/nomnom-1.8.1.tgz#2151f722472ba79e50a76fc125bb8c8f2e4dc2a7"
@@ -8725,6 +8779,11 @@ pkg-up@^2.0.0:
   dependencies:
     find-up "^2.1.0"
 
+platform@^1.3.3:
+  version "1.3.5"
+  resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.5.tgz#fb6958c696e07e2918d2eeda0f0bc9448d733444"
+  integrity sha512-TuvHS8AOIZNAlE77WUDiR4rySV/VMptyMfcfeoMgs4P8apaZM3JrnbzBiixKUv+XR6i+BXrQh8WAnjaSPFO65Q==
+
 pluralize@^7.0.0:
   version "7.0.0"
   resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777"
@@ -9176,6 +9235,14 @@ react-dom@^16.4.0:
     prop-types "^15.6.2"
     schedule "^0.5.0"
 
+react-emotion@^9.2.12:
+  version "9.2.12"
+  resolved "https://registry.yarnpkg.com/react-emotion/-/react-emotion-9.2.12.tgz#74d1494f89e22d0b9442e92a33ca052461955c83"
+  integrity sha512-qt7XbxnEKX5sZ73rERJ92JMbEOoyOwG3BuCRFRkXrsJhEe+rFBRTljRw7yOLHZUCQC4GBObZhjXIduQ8S0ZpYw==
+  dependencies:
+    babel-plugin-emotion "^9.2.11"
+    create-emotion-styled "^9.2.8"
+
 react-error-overlay@4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-4.0.0.tgz#d198408a85b4070937a98667f500c832f86bd5d4"
@@ -9205,7 +9272,7 @@ react-inspector@^2.2.2:
     babel-runtime "^6.26.0"
     is-dom "^1.0.9"
 
-react-is@^16.5.2:
+react-is@^16.3.1, react-is@^16.5.2:
   version "16.5.2"
   resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.5.2.tgz#e2a7b7c3f5d48062eb769fcb123505eb928722e3"
   integrity sha512-hSl7E6l25GTjNEZATqZIuWOgSnpXb3kD0DVCujmg46K5zLxsbiKaaT6VO9slkSBDPZfYs30lwfJwbOFOnoEnKQ==
@@ -10725,6 +10792,21 @@ style-loader@^0.23.0:
     loader-utils "^1.1.0"
     schema-utils "^0.4.5"
 
+styled-components@^3.4.10:
+  version "3.4.10"
+  resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-3.4.10.tgz#9a654c50ea2b516c36ade57ddcfa296bf85c96e1"
+  integrity sha512-TA8ip8LoILgmSAFd3r326pKtXytUUGu5YWuqZcOQVwVVwB6XqUMn4MHW2IuYJ/HAD81jLrdQed8YWfLSG1LX4Q==
+  dependencies:
+    buffer "^5.0.3"
+    css-to-react-native "^2.0.3"
+    fbjs "^0.8.16"
+    hoist-non-react-statics "^2.5.0"
+    prop-types "^15.5.4"
+    react-is "^16.3.1"
+    stylis "^3.5.0"
+    stylis-rule-sheet "^0.0.10"
+    supports-color "^3.2.3"
+
 styled-jsx@3.1.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-3.1.0.tgz#c295e4170298b5bb858f848c4b73e423a73a68f3"
@@ -10761,6 +10843,13 @@ supports-color@^2.0.0:
   resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
   integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=
 
+supports-color@^3.2.3:
+  version "3.2.3"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6"
+  integrity sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=
+  dependencies:
+    has-flag "^1.0.0"
+
 supports-color@^5.3.0, supports-color@^5.4.0, supports-color@^5.5.0:
   version "5.5.0"
   resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"


Test patch:
diff --git a/packages/material-ui/src/ButtonBase/ButtonBase.test.js b/packages/material-ui/src/ButtonBase/ButtonBase.test.js
--- a/packages/material-ui/src/ButtonBase/ButtonBase.test.js
+++ b/packages/material-ui/src/ButtonBase/ButtonBase.test.js
@@ -633,7 +633,10 @@ describe('<ButtonBase />', () => {
       wrapper.setProps({
         children: 'bar',
       });
-      assert.strictEqual(rerender.updates.length, 1);
+      assert.strictEqual(
+        rerender.updates.filter(update => update.displayName !== 'NoSsr').length,
+        1,
+      );
     });
   });
 });
diff --git a/packages/material-ui/src/styles/MuiThemeProvider.test.js b/packages/material-ui/src/styles/MuiThemeProvider.test.js
--- a/packages/material-ui/src/styles/MuiThemeProvider.test.js
+++ b/packages/material-ui/src/styles/MuiThemeProvider.test.js
@@ -82,11 +82,11 @@ describe('<MuiThemeProvider />', () => {
       );
 
       assert.notStrictEqual(markup.match('Hello World'), null);
-      assert.strictEqual(sheetsRegistry.registry.length, 3);
+      assert.strictEqual(sheetsRegistry.registry.length, 2);
       assert.strictEqual(sheetsRegistry.toString().length > 4000, true);
-      assert.strictEqual(sheetsRegistry.registry[0].classes.root, 'MuiTouchRipple-root-30');
+      assert.strictEqual(sheetsRegistry.registry[0].classes.root, 'MuiButtonBase-root-27');
       assert.deepEqual(
-        sheetsRegistry.registry[1].classes,
+        sheetsRegistry.registry[0].classes,
         {
           disabled: 'MuiButtonBase-disabled-28',
           focusVisible: 'MuiButtonBase-focusVisible-29',
@@ -94,7 +94,7 @@ describe('<MuiThemeProvider />', () => {
         },
         'the class names should be deterministic',
       );
-      assert.strictEqual(sheetsRegistry.registry[2].classes.root, 'MuiButton-root-1');
+      assert.strictEqual(sheetsRegistry.registry[1].classes.root, 'MuiButton-root-1');
     });
   });
 
diff --git a/test/utils/rerender.js b/test/utils/rerender.js
--- a/test/utils/rerender.js
+++ b/test/utils/rerender.js
@@ -1,9 +1,11 @@
+/* eslint-disable no-underscore-dangle */
+
 import React from 'react';
 import getDisplayName from '../../packages/material-ui/src/utils/getDisplayName';
 
 function createComponentDidUpdate(instance) {
   return function componentDidUpdate() {
-    const displayName = getDisplayName(this);
+    const displayName = getDisplayName(this._reactInternalFiber.type);
     instance.updates.push({
       displayName,
     });
diff --git a/test/utils/setup.js b/test/utils/setup.js
--- a/test/utils/setup.js
+++ b/test/utils/setup.js
@@ -1,5 +1,7 @@
 const createDOM = require('./createDOM');
 
+process.browser = true;
+
 // eslint-disable-next-line no-underscore-dangle
 global.__MUI_USE_NEXT_TYPOGRAPHY_VARIANTS__ = true;
 createDOM();