<p dir="auto">Hi,</p>
<p dir="auto">First I must say thanks for the awesome effort that has been put into v1 rewrite</p>

<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I have searched the <a href="https://github.com/callemall/material-ui/issues">issues</a> of this repository and believe that this is not a duplicate.</li>
</ul>
<h2 dir="auto">Expected Behavior</h2>
<p dir="auto">Consider a SSR scenario with async component loading using ReactDOM.hydrate, with a component hierarchy a bit like this:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="Layout
-Header
--ItemTitle (async)
-Main
--ListFilterControls
--List (async)
---ListItem
---ListItem
---ListItem
--ListLoadMoreControls
-Footer"><pre class="notranslate"><code class="notranslate">Layout
-Header
--ItemTitle (async)
-Main
--ListFilterControls
--List (async)
---ListItem
---ListItem
---ListItem
--ListLoadMoreControls
-Footer
</code></pre></div>
<p dir="auto">As the async component load time is variable (database vs cache vs network), the order that React encounters the components can differ between the browser vs server</p>
<h2 dir="auto">Current Behavior</h2>
<p dir="auto">Initial render of the raw HTML/CSS from SSR is okay, components have the correct styles sheets applied.</p>
<p dir="auto">However after removing the SSR injected styles sheets, some of the later order (ie lower down the page) component styles get corrupted/go missing.</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="const jssStyles = document.getElementById(&quot;jss-server-side&quot;);
jssStyles.parentNode.removeChild(jssStyles);"><pre class="notranslate"><code class="notranslate">const jssStyles = document.getElementById("jss-server-side");
jssStyles.parentNode.removeChild(jssStyles);
</code></pre></div>
<p dir="auto">If the component with corrupted styles is remounted (eg by a page transition/click action) then the component styles are rebound and corrected</p>
<h2 dir="auto">Diagnostic</h2>
<p dir="auto">Comparing the raw HTML/CSS from the SSR, I can see that the CSS style sheet name matches the reference used by the HTML, how it should be, and this works.</p>
<p dir="auto">When comparing what happens in the hydrated HTML/CSS, I can see that the HTML css classname remains the same as the server, however the [head.style.css] name has changed, it has got a new ruleCounter. This causes the component to lose its styles. (until a remount/state change)</p>
<p dir="auto">If i inject a custom createGenerateClassName for the purpose of tracing the ruleCounter i can see:</p>
<ul dir="auto">
<li>The style sheet ruleCounter for most of the outer components (Layout,Header,Main,Footer) are always the same on both server and client</li>
<li>The style sheet ruleCounter for some of the later components (List,ListItem,ListLoadMoreControls) are different between server and client</li>
<li>The amount of rules are the same, its just the order that they are processed is different.</li>
</ul>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="ItemsControls-layout_controls-147
NewContent-layout_container-148
NewContent-layout_cell-149
LoadMore-layout_container-150
LoadMore-layout_cell-151
MuiDialog-root-152
MuiDialog-paper-153
MuiDialog-paperWidthXs-154
MuiDialog-paperWidthSm-155
MuiDialog-paperWidthMd-156
MuiDialog-fullWidth-157
MuiDialog-fullScreen-158
MuiSnackbar-root-159
MuiSnackbar-anchorTopCenter-160
MuiSnackbar-anchorBottomCenter-161
MuiSnackbar-anchorTopRight-162
MuiSnackbar-anchorBottomRight-163
MuiSnackbar-anchorTopLeft-164
MuiSnackbar-anchorBottomLeft-165
Tile-animation_expand-166
Tile-animation_expandopen-167
Tile-layout_avatar-168
Tile-layout_age-169
Tile-layout_age2-170
Tile-layout_age2_passed-171
Tile-layout_summary-172
Tile-layout_summary2-173
Tile-layout_summary_row-174
Tile-layout_summary_cell-175
Avatar-avatar_root-176
Summary-layout_summary-177
Summary-layout_summary2-178
Summary-layout_summary_row-179
Summary-layout_summary_cell-180"><pre class="notranslate"><code class="notranslate">ItemsControls-layout_controls-147
NewContent-layout_container-148
NewContent-layout_cell-149
LoadMore-layout_container-150
LoadMore-layout_cell-151
MuiDialog-root-152
MuiDialog-paper-153
MuiDialog-paperWidthXs-154
MuiDialog-paperWidthSm-155
MuiDialog-paperWidthMd-156
MuiDialog-fullWidth-157
MuiDialog-fullScreen-158
MuiSnackbar-root-159
MuiSnackbar-anchorTopCenter-160
MuiSnackbar-anchorBottomCenter-161
MuiSnackbar-anchorTopRight-162
MuiSnackbar-anchorBottomRight-163
MuiSnackbar-anchorTopLeft-164
MuiSnackbar-anchorBottomLeft-165
Tile-animation_expand-166
Tile-animation_expandopen-167
Tile-layout_avatar-168
Tile-layout_age-169
Tile-layout_age2-170
Tile-layout_age2_passed-171
Tile-layout_summary-172
Tile-layout_summary2-173
Tile-layout_summary_row-174
Tile-layout_summary_cell-175
Avatar-avatar_root-176
Summary-layout_summary-177
Summary-layout_summary2-178
Summary-layout_summary_row-179
Summary-layout_summary_cell-180
</code></pre></div>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="ItemsControls-layout_controls-147
NewContent-layout_container-148
NewContent-layout_cell-149
Tile-animation_expand-150
Tile-animation_expandopen-151
Tile-layout_avatar-152
Tile-layout_age-153
Tile-layout_age2-154
Tile-layout_age2_passed-155
Tile-layout_summary-156
Tile-layout_summary2-157
Tile-layout_summary_row-158
Tile-layout_summary_cell-159
Avatar-avatar_root-160
Summary-layout_summary-161
Summary-layout_summary2-162
Summary-layout_summary_row-163
Summary-layout_summary_cell-164
LoadMore-layout_container-165
LoadMore-layout_cell-166
MuiDialog-root-167
MuiDialog-paper-168
MuiDialog-paperWidthXs-169
MuiDialog-paperWidthSm-170
MuiDialog-paperWidthMd-171
MuiDialog-fullWidth-172
MuiDialog-fullScreen-173
MuiSnackbar-root-174
MuiSnackbar-anchorTopCenter-175
MuiSnackbar-anchorBottomCenter-176
MuiSnackbar-anchorTopRight-177
MuiSnackbar-anchorBottomRight-178
MuiSnackbar-anchorTopLeft-179
MuiSnackbar-anchorBottomLeft-180"><pre class="notranslate"><code class="notranslate">ItemsControls-layout_controls-147
NewContent-layout_container-148
NewContent-layout_cell-149
Tile-animation_expand-150
Tile-animation_expandopen-151
Tile-layout_avatar-152
Tile-layout_age-153
Tile-layout_age2-154
Tile-layout_age2_passed-155
Tile-layout_summary-156
Tile-layout_summary2-157
Tile-layout_summary_row-158
Tile-layout_summary_cell-159
Avatar-avatar_root-160
Summary-layout_summary-161
Summary-layout_summary2-162
Summary-layout_summary_row-163
Summary-layout_summary_cell-164
LoadMore-layout_container-165
LoadMore-layout_cell-166
MuiDialog-root-167
MuiDialog-paper-168
MuiDialog-paperWidthXs-169
MuiDialog-paperWidthSm-170
MuiDialog-paperWidthMd-171
MuiDialog-fullWidth-172
MuiDialog-fullScreen-173
MuiSnackbar-root-174
MuiSnackbar-anchorTopCenter-175
MuiSnackbar-anchorBottomCenter-176
MuiSnackbar-anchorTopRight-177
MuiSnackbar-anchorBottomRight-178
MuiSnackbar-anchorTopLeft-179
MuiSnackbar-anchorBottomLeft-180
</code></pre></div>
<h2 dir="auto">Ideas</h2>
<p dir="auto">Some attempts to workaround this problem:</p>
<ol dir="auto">
<li>Disabling SSR, the component tree and styles generate completely on the client, so all ruleCounters match, not ideal as i lose SSR</li>
<li>Force consistent component rendering order, hard as async loading time is variable, also need to consider how fiber would work</li>
<li>Force re-render of whole component tree after ReactDOM.hydrate, not ideal, as perf hit</li>
<li>Custom createGenerateClassName which removes non-deterministic ruleCounter</li>
</ol>
<p dir="auto">I am leaning towards no4<br>
This works in development-mode, however [sheet.options.meta] is not calculated in production-mode<br>
Could there be a way to allow [sheet.options.meta] in production mode?<br>
<a href="https://github.com/callemall/material-ui/blob/v1-beta/src/styles/withStyles.js#L188-L193">https://github.com/callemall/material-ui/blob/v1-beta/src/styles/withStyles.js#L188-L193</a><br>
(note: all my calls to withStyles already use [options.name] so there shouldnt be any clashes)</p>
<h2 dir="auto">Your Environment</h2>
<table role="table">
<thead>
<tr>
<th>Tech</th>
<th>Version</th>
</tr>
</thead>
<tbody>
<tr>
<td>Material-UI</td>
<td>1.0.0-beta.14</td>
</tr>
<tr>
<td>React</td>
<td>16.0.0</td>
</tr>
<tr>
<td>browser</td>
<td>IE11/Chrome/Firefox</td>
</tr>
</tbody>
</table>
<ul dir="auto">
<li>[x ] I have searched the <a href="https://github.com/callemall/material-ui/issues">issues</a> of this repository and believe that this is not a duplicate.</li>
</ul>
<h2 dir="auto">Expected Behavior</h2>
<p dir="auto">Expected classnames to be unique pr generator returned.</p>
<h2 dir="auto">Current Behavior</h2>
<p dir="auto">The generator function returned by createGenerateClassName is based on a counter. This causes two functions returned by this function to generate the same colliding classnames. We have a rather large app consisting of several webpack bundles. Each small part of the application is suppose to be isolated and not pollute the global environement. Each part is defined as a separate module with its own dependencies, hence its very difficult to keep a consistent and shared version of material-ui. When several versions of material-ui exist on a page this causes style tags with colliding class definitions to be inserted.</p>
<h2 dir="auto">Context</h2>
    
<h2 dir="auto">Your Environment</h2>

<table role="table">
<thead>
<tr>
<th>Tech</th>
<th>Version</th>
</tr>
</thead>
<tbody>
<tr>
<td>Material-UI</td>
<td>1.0.0-beta.17</td>
</tr>
<tr>
<td>React</td>
<td>all</td>
</tr>
<tr>
<td>browser</td>
<td>all</td>
</tr>
<tr>
<td>etc</td>
<td></td>
</tr>
</tbody>
</table>
<p dir="auto">This issue could be solved by generating a id pr generator so that the chance of getting colliding classes is minimized. Instead of having a hardcoded 'c' prefix the createGenerateClassName can be changed to something like this; (Changed lines prefixed with a +)</p>
<div class="highlight highlight-source-diff notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="

import warning from 'warning';
import type StyleSheet from 'jss/lib/StyleSheet';
import type { Rule, generateClassName } from 'jss/lib/types';

let generatorCounter = 0;

// Returns a function which generates unique class names based on counters.
// When new generator function is created, rule counter is reset.
// We need to reset the rule counter for SSR for each request.
//
// It's an improved version of
// https://github.com/cssinjs/jss/blob/4e6a05dd3f7b6572fdd3ab216861d9e446c20331/src/utils/createGenerateClassName.js

+const getLetter = () =&gt; &quot;abcdefghijklmnopqrstuvwxyz&quot;.charAt(Math.floor(Math.random()* 26));
export default function createGenerateClassName(): generateClassName {
  let ruleCounter = 0;
+  const generatorPrefix = [getLetter(), getLetter(), getLetter()].join(&quot;&quot;)

  if (process.env.NODE_ENV === 'production' &amp;&amp; typeof window !== 'undefined') {
    generatorCounter += 1;

    if (generatorCounter &gt; 2) {
      // eslint-disable-next-line no-console
      console.error(
        [
          'Material-UI: we have detected more than needed creation of the class name generator.',
          'You should only use one class name generator on the client side.',
          'If you do otherwise, you take the risk to have conflicting class names in production.',
        ].join('\n'),
      );
    }
  }

  return (rule: Rule, sheet?: StyleSheet): string =&gt; {
    ruleCounter += 1;
    warning(
      ruleCounter &lt; 1e10,
      [
        'Material-UI: you might have a memory leak.',
        'The ruleCounter is not supposed to grow that much.',
      ].join(''),
    );

    if (process.env.NODE_ENV === 'production') {
+      return `${generatorPrefix}${ruleCounter}`;
    }

    if (sheet &amp;&amp; sheet.options.meta) {
      let meta = sheet.options.meta;
      // Sanitize the string as will be used in development to prefix the generated
      // class name.
      meta = meta.replace(new RegExp(/[!&quot;#$%&amp;'()*+,./:; &lt;=&gt;?@[\\\]^`{|}~]/g), '-');

      return `${meta}-${rule.key}-${ruleCounter}`;
    }

    return `${rule.key}-${ruleCounter}`;
  };"><pre class="notranslate">import warning from 'warning';
import type StyleSheet from 'jss/lib/StyleSheet';
import type { Rule, generateClassName } from 'jss/lib/types';

let generatorCounter = 0;

// Returns a function which generates unique class names based on counters.
// When new generator function is created, rule counter is reset.
// We need to reset the rule counter for SSR for each request.
//
// It's an improved version of
// https://github.com/cssinjs/jss/blob/4e6a05dd3f7b6572fdd3ab216861d9e446c20331/src/utils/createGenerateClassName.js

<span class="pl-mi1"><span class="pl-mi1">+</span>const getLetter = () =&gt; "abcdefghijklmnopqrstuvwxyz".charAt(Math.floor(Math.random()* 26));</span>
export default function createGenerateClassName(): generateClassName {
  let ruleCounter = 0;
<span class="pl-mi1"><span class="pl-mi1">+</span>  const generatorPrefix = [getLetter(), getLetter(), getLetter()].join("")</span>

  if (process.env.NODE_ENV === 'production' &amp;&amp; typeof window !== 'undefined') {
    generatorCounter += 1;

    if (generatorCounter &gt; 2) {
      // eslint-disable-next-line no-console
      console.error(
        [
          'Material-UI: we have detected more than needed creation of the class name generator.',
          'You should only use one class name generator on the client side.',
          'If you do otherwise, you take the risk to have conflicting class names in production.',
        ].join('\n'),
      );
    }
  }

  return (rule: Rule, sheet?: StyleSheet): string =&gt; {
    ruleCounter += 1;
    warning(
      ruleCounter &lt; 1e10,
      [
        'Material-UI: you might have a memory leak.',
        'The ruleCounter is not supposed to grow that much.',
      ].join(''),
    );

    if (process.env.NODE_ENV === 'production') {
<span class="pl-mi1"><span class="pl-mi1">+</span>      return `${generatorPrefix}${ruleCounter}`;</span>
    }

    if (sheet &amp;&amp; sheet.options.meta) {
      let meta = sheet.options.meta;
      // Sanitize the string as will be used in development to prefix the generated
      // class name.
      meta = meta.replace(new RegExp(/[!"#$%&amp;'()*+,./:; &lt;=&gt;?@[\\\]^`{|}~]/g), '-');

      return `${meta}-${rule.key}-${ruleCounter}`;
    }

    return `${rule.key}-${ruleCounter}`;
  };</pre></div>
<p dir="auto">As stated this does absolutely not guarantee that you will not have colliding classnames, but makes it less likely. If we also include initial timestamp in the generator prefix it will be better, but the production classnames will also be longer;</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content=" const generatorPrefix = [new Date().geTime(), getLetter(), getLetter(), getLetter()].join(&quot;&quot;)"><pre class="notranslate"><code class="notranslate"> const generatorPrefix = [new Date().geTime(), getLetter(), getLetter(), getLetter()].join("")
</code></pre></div>
<p dir="auto">If this is something you would consider taking into the codebase advice me on how you would like it solved and I can create a PR</p>