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.

Server Rendering styles need to come after content in <body />

See original GitHub issue

When following the instructions on the next docs, I realized there are super strange issues loading styles depending on where I put the JssProvider. Looks like it needs to go exactly one level above MuiThemeProvider and needs to render client-side as well as server-side.

This isn’t clearly explained in the docs so I spent a lot of time trying to figure out what was wrong.

  • I have searched the issues of this repository and believe that this is not a duplicate.

Expected Behavior

It works as it does when doing client-side only rendering.

Current Behavior

All sorts of oddities. Expand panels upside down, styles not loading properly, icons too large, red boxes behind badges, and more.

It seems to be reacting differently also depending on which components are loaded on that particular page.

Steps to Reproduce (for bugs)

  1. Roughly follow the Server Rendering instructions, but not 100% exactly.
  2. Experience a multitude of issues.
  3. Try everything to fix it.
  4. Eventually cave and put the JssProvider also client-side.
  5. Find it worked in one situation and not another.
  6. Fix the problem again by using dangerouslySetInnerHTML, but then find out changing the route breaks it again.
  7. Post a call for help on GitHub.

Context

I was up all night trying to deploy some applications for a demo and couldn’t figure out how to get the various apps running Material UI to show up properly with server-side rendered styles.

Your Environment

Tech Version
Material-UI 1.0.0-beta.41
React 16.2.0
browser Chrome 65

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:10 (2 by maintainers)

github_iconTop GitHub Comments

3reactions
guico33commented, Apr 16, 2018

Would a two-pass rendering be an acceptable solution?

From the react doc on hydrate():

If you intentionally need to render something different on the server and the client, you can do a two-pass rendering. Components that render something different on the client can read a state variable like this.state.isClient, which you can set to true in componentDidMount(). This way the initial render pass will render the same content as the server, avoiding mismatches, but an additional pass will happen synchronously right after hydration. Note that this approach will make your components slower because they have to render twice, so use it with caution.

The following does work though I’m not sure about performance.

class Root extends Component {
  state = { isClient: false };

  componentDidMount() {
    this.setState({ isClient: true });
    this.cleanStyles ();
  }

  cleanStyles = () => {
    const jssStyles = document.getElementById('jss-server-side');
    if (jssStyles && jssStyles.parentNode) {
      jssStyles.parentNode.removeChild(jssStyles);
    }
  };

  render() {
    const { isClient } = this.state;
    return (
      isClient && (
        <MuiThemeProvider theme={theme}>
             <App />
        </MuiThemeProvider>
      )
    );
  }
}

// hydrate used 
hydrate(<Root />, document.getElementById('root'));

Server code is as described in Material-UI documentation.

2reactions
Sawtaytoescommented, Apr 18, 2018

@oliviertassinari, I think this needs to be reopened. There appear to be issues running my configuration to the exact spec of the docs. I apologize for any earlier comments about hydrate. Looks like that was an issue, but not the exact issue I’m coming across.

All of my server-side classes are rendered as .Mui* like I’d see in development mode, and they render properly. The issue comes when in NODE_ENV production and rendering on the client. I was seeing .jss* classes showing up, and many Material UI elements weren’t styled correctly. I’m very confused about this.


Ideal Code Based on Docs

Depending on if the DOM is available or not I created these components to handle rendering:

MaterialUiServerThemeRenderer.js

import JssProvider from 'react-jss/lib/JssProvider'
import PropTypes from 'prop-types'
import React from 'react'
import { createGenerateClassName } from 'material-ui/styles'
import { MuiThemeProvider } from 'material-ui/styles'

import muiTheme from '@shared/utils/styles/muiTheme'
import { getSheetsRegistry } from '@shared/utils/materialUi/sheetsRegistry'

const generateClassName = createGenerateClassName()

const MaterialUiServerThemeRenderer = ({ children }) => (
  <JssProvider
    generateClassName={generateClassName}
    registry={getSheetsRegistry()}
  >
    <MuiThemeProvider
      sheetsManager={new Map()}
      theme={muiTheme}
    >
      {children}
    </MuiThemeProvider>
  </JssProvider>
)

MaterialUiServerThemeRenderer.propTypes = {
  children: PropTypes.node.isRequired,
}

export default MaterialUiServerThemeRenderer

MaterialUiClientThemeRenderer.js

import PropTypes from 'prop-types'
import React from 'react'
import { MuiThemeProvider } from 'material-ui/styles'

import muiTheme from '@shared/utils/styles/muiTheme'
import RemoveMuiThemeServerStyles from './RemoveMuiThemeServerStyles'

const MaterialUiClientThemeRenderer = ({ children }) => (
  <RemoveMuiThemeServerStyles>
    <MuiThemeProvider theme={muiTheme}>
      {children}
    </MuiThemeProvider>
  </RemoveMuiThemeServerStyles>
)

MaterialUiClientThemeRenderer.propTypes = {
  children: PropTypes.node.isRequired,
}

export default MaterialUiClientThemeRenderer

Code I Ended Up Using

Since following the docs didn’t work correctly, I ended up adding JssProvider on client render and not removing the <style /> tag. Firstly, adding JssProvider changed all .jss* classes into .Mui*. This fixed quite a few issues. Second, as some styles get hydrated on render, if I remove the style tag, those important styles go away. Because of this, I left it in. To clarify, styling issues only occur in NODE_ENV production and for whatever reason, don’t seem to occur when server rendering. This might be because I’m only explicitly in defining NODE_ENV as production in Webpack, not via the command line locally.

MaterialUiClientThemeRenderer.js

import JssProvider from 'react-jss/lib/JssProvider'
import PropTypes from 'prop-types'
import React from 'react'
import { MuiThemeProvider } from 'material-ui/styles'

import muiTheme from '@shared/utils/styles/muiTheme'

const MaterialUiClientThemeRenderer = ({ children }) => (
  <JssProvider>
    <MuiThemeProvider theme={muiTheme}>
      {children}
    </MuiThemeProvider>
  </JssProvider>
)

MaterialUiClientThemeRenderer.propTypes = {
  children: PropTypes.node.isRequired,
}

export default MaterialUiClientThemeRenderer
Read more comments on GitHub >

github_iconTop Results From Across the Web

Server Rendering styles need to come after content in <body />
It works as it does when doing client-side only rendering. Current Behavior. All sorts of oddities. Expand panels upside down, styles not ...
Read more >
Server rendering - Material UI - MUI
The key step in server-side rendering is to render the initial HTML of the component before we send it to the client-side. To...
Read more >
Rendering on the Web
Server rendering generates the full HTML for a page on the server in response to navigation. This avoids additional round-trips for data ...
Read more >
Server-side rendered styled-components with Nextjs
Basically you need to add a custom pages/_document.js (if you don't have one). Then copy the logic for styled-components to inject the server...
Read more >
How To Render CSS on React App Servers | DigitalOcean
In this article, we will go over the challenges of rendering CSS on the server and then demonstrate how we can overcome these...
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 Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found