React-16 + Material-UI v4.10.X CSS not loading issue in Server Side Rendering

Server side rendering in React 16 is always a tricky, it behaves different than client side rendering. One of the typical issue we face for SSR apps is CSS not loading and images are not rendering.

Interesting observations are CSS are being rendered properly in landing page, however in any other routes CSS are missing. This issue is very obvious when we are using Material-ui library. In every new release Material-UI is making drastic changes and hence its being little tough to upgrade library.

The complete Code for Server Side Rendering in React16 and Material-UI is here

For CSS not loading in SSR app another mistake could be not referring to your build folder in server side code, hence kindly first check below article to make necessary changes which is mandatory.

CSS & Images not loading in React SSR App

However to support Material-UI v4.10.x we need to change two files in the code given in above link. We have to change request handler and App.js “<Provider> part”

Here is the updated code given below

requestHandler.js File Changes

'use strict';

import React from 'react';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import { applyMiddleware, createStore } from 'redux';
import { renderToString } from 'react-dom/server';
import { ServerStyleSheets, ThemeProvider, createMuiTheme } from '@material-ui/core/styles';

// import JssProvider from 'react-jss/lib/JssProvider';
import path from 'path'
import fs from 'fs'
// import {
//   MuiThemeProvider,
//   createMuiTheme,
//   createGenerateClassName,
// } from '@material-ui/core/styles';
import green from '@material-ui/core/colors/green';
import red from '@material-ui/core/colors/red';
import { StaticRouter, matchPath } from 'react-router-dom';
import DocumentMeta from 'react-document-meta';
import reducers from '../Reducers';
import routes from '../Routes';
import routesConfigs from './routesConfig';

const middleware = applyMiddleware(thunk);

// const escapeRegex = /([[\].#*$><+~=|^:(),"'`\s])/g;
// let classCounter = 0;

// export const generateClassName = (rule, styleSheet) => {
//   classCounter += 1;

//   if (process.env.NODE_ENV === 'production') {
//     return `c${classCounter}`;
//   }

//   if (styleSheet && styleSheet.options.classNamePrefix) {
//     let prefix = styleSheet.options.classNamePrefix;
//     // Sanitize the string as will be used to prefix the generated class name.
//     prefix = prefix.replace(escapeRegex, '-');

//     if (prefix.match(/^Mui/)) {
//       return `${prefix}-${rule.key}`;
//     }

//     return `${prefix}-${rule.key}-${classCounter}`;
//   }

//   return `${rule.key}-${classCounter}`;
// };

function renderView(req, res, state) {
  const sheets = new ServerStyleSheets();
  // Create a theme instance.
  const theme = createMuiTheme({
    palette: {
      primary: green,
      accent: red,
      type: 'light',
    },
  });

  // STEP-1 CREATE A REDUX STORE ON THE SERVER
  const store = createStore(reducers, state, middleware);
  // const sheetsRegistry = new SheetsRegistry();
  // Create a sheetsManager instance.
  // const sheetsManager = new Map();
  //commented below and using the function pasted above to solve css problem in sidebar and for buttons component
  // const generateClassName = createGenerateClassName();
  // STEP-2 GET INITIAL STATE FROM THE STORE

  const initialState = JSON.stringify(store.getState()).replace(/<\/script/g, '<\\/script').replace(/<!--/g, '<\\!--');
  // STEP-3 IMPLEMENT REACT-ROUTER ON THE SERVER TO INTERCEPT CLIENT REQUESTs AND DEFINE WHAT TO DO WITH THEM
  const context = {};
  
  const reactComponent = renderToString(
    sheets.collect(
    // <JssProvider generateClassName={generateClassName}>
      <ThemeProvider theme={theme}>
        <Provider store={store}>
          <StaticRouter
            location={req.url}
            context={context}>
            {routes}
          </StaticRouter>
        </Provider>
      </ThemeProvider>
      ),
    // </JssProvider>
  );
  // const css = sheetsRegistry.toString()
  const css = sheets.toString();
  const reactMetaComponent = DocumentMeta.renderToStaticMarkup();
  if (context.url) {
    // can use the `context.status` that
    // we added in RedirectWithStatus
    redirect(context.status, context.url);
  } else {
    //https://crypt.codemancers.com/posts/2016-09-16-react-server-side-rendering/
    //res.status(200).render('index', { reactComponent, reactMetaComponent, initialState });
    fs.readFile(path.resolve('build/index.html'), 'utf8', (err, data) => {
      if (err) {
        return res.status(500).send('An error occurred')
      }
      const replacedData = data.replace(
        '<div id="root"></div>',
        `<div id="root">${reactComponent}</div>
        <style id="jss-server-side">${css}</style>
        <script>
          window.INITIAL_STATE = ${initialState}
        </script>`
      );
      const replacedMetaTagData = replacedData
        .replace(`<meta id="reactMetaTags"/>`,
          `${reactMetaComponent}`);
      res.send(replacedMetaTagData);
    })
  }
}

function handleRender(req, res) {
  // filter matching paths
  // and check if components have data requirement
  const components =
    routesConfigs.filter(route => matchPath(req.path, route))
      .map(route => route.component);
  if (components.length > 0 && (components[0].fetchData instanceof Function)) {
    components[0]
      .fetchData(req.query, req.path)
      .then((response) => {
        renderView(req, res, response);
      })
      .catch((error) => {
        try {
          console.log('--- ssr render error --- ', error);
        } catch (e) {
          console.log('--- ssr render error catch--- ', e);
        }
        renderView(req, res, {});
      });
  } else {
    renderView(req, res, {});
  }
}

module.exports = handleRender;

Observe that we removed using JssProvider , generateClassName, Stylesheet Manager etc. The latest changes are much simpler

App.js file changes

import React, { Component } from 'react';
import thunk from 'redux-thunk';
import { Provider } from 'react-redux';
// import JssProvider from 'react-jss/lib/JssProvider';
// import {
//   MuiThemeProvider,
//   createMuiTheme,
//   createGenerateClassName
// } from '@material-ui/core/styles';
import { ThemeProvider, createMuiTheme } from '@material-ui/core/styles';
import { applyMiddleware, createStore } from 'redux';
import { BrowserRouter } from 'react-router-dom'
import green from '@material-ui/core/colors/green'
import red from '@material-ui/core/colors/red';
import Routes from './Routes';
import reducers from './Reducers';

const middleware = applyMiddleware(thunk);
const initialState = window.INITIAL_STATE;
const store = createStore(reducers, initialState, middleware);

// Create a theme instance.
const theme = createMuiTheme({
  palette: {
    primary: green,
    accent: red,
    type: 'light',
  },
});
// const generateClassName = createGenerateClassName();

class App extends Component {
  componentDidMount() {
    //Not required to remove css since it is already came from sssr
    // const jssStyles = document.getElementById('jss-server-side');
    // if (jssStyles && jssStyles.parentNode) {
    //   jssStyles.parentNode.removeChild(jssStyles);
    // }
    const jssStyles = document.querySelector('#jss-server-side');
    if (jssStyles) {
      jssStyles.parentElement.removeChild(jssStyles);
    }
  }
  
  render() {
    return (
      <Provider store={store}>
        <BrowserRouter>
          {/* <JssProvider generateClassName={generateClassName}> */}
            <ThemeProvider theme={theme} >
              {Routes}
            </ThemeProvider>
          {/* </JssProvider> */}
        </BrowserRouter>
      </Provider>
    );
  }
}

export default App;

Again we no need to use JssProvider in client side as well.

These changes will solve the issue.

Animation and The Role of GPU – A Performance factor

Animation and The Role of GPU – A Performance factor

Just take a look into the given CSS which rotates your div by 360deg

.box_rotate {
 -webkit-transform: rotate(360deg); 
 -moz-transform: rotate(360deg); 
 -ms-transform: rotate(360deg); 
 -o-transform: rotate(360deg); 
 transform: rotate(360deg);
}

Code Example – JSFiddle

Have you ever wondered what happens in your computer when we see even a simple animation?

Your computer algorithm calculates every pixel and performs CPU intensive operation over each pixel. Ok what such operations it does ?. It calculates every pixel and performs Discrete Fourier Transformation on each pixel of the image. If your image is 512X512  = 2,62,144 pixels and the algorithms operates several frames per second, like 50 frames per second which depends upon the type of hardware/algorithm being used. It operates every pixel several times per second ( like every pixel 60 times per second ) now image for an image with size 512X512 = 2,62,144pixels and each pixel operated 60 times per second this gives you unbelievably huge number.

Assume that you operated only 1 second,

2,62,144 * 60 = 1,57,28,640 times and these many times it is not just a simple operation, they may apply Fourier transformation on each pixel. 

Another important factor to note that the Image rotation, Image scaling or any graphics related operations the the matrix calculations will come into picture. The rotation matrix and the image matrix will be multiplied to get the resulting image

rotation matrix
rotation matrix

An image matrix will be a huge one, like 512 x 512 x 3 for a given 512X512 image. the 3 in matrix indicates image is RGB coloured. It is not required to explain how matrix multiplications are CPU extensive, Theoretically, matrix-matrix multiplication uses O(N^3) operations on O(N^2) data

Comparison of GPU Matrix Multiplication operation over CPU

Why GPU and Why not CPU.

GPU are specifically made for performing graphics operations like moving pixels and applying transforms. Since CPU will be doing many more tasks like file operations, audio, calculations etc it is better to separate out your animated objects and give it to a different GPU layer. Imagine in the screen of 1024X800, somewhere some 200X100 size portion is animating, rather than giving 1024X800 space and find 200×100 space and then operate , it is nice trick to create separate layer of 200×100 space and perform animation independently in that layer.

So the load on your CPU will be reduced and the performance of the system will be improved. The difference between CSS3 and jquery animations are , CSS3 utilises GPU but jquery doesn’t, there another javascript based latest framework available called GSAP which is 20x faster than query. 

 

Comparison of jquery, CSS and GSAP Animation Performance :

http://codepen.io/GreenSock/pen/srfxA

Check our another article – how-to-make-you-web-site-faster-html-javascript-tricks

Reference : https://css-tricks.com/myth-busting-css-animations-vs-javascript/

HTML Style , CSS properties Browser compatibility

CSS Proprties and Browser compatibility

CSS Properties :

1) content

  Never use content to set background image for div, instead use background-image :
Example 
			



 
.divItem{
    position: absolute;
    top: 12px;
    right: 11px;
    width: 40px;
    height: 40px;
    content: url(../../images/elective.png)
}
 
The above properties works fine in Chrome, but in firefox the image doesn't appear.
.divItem{
    position: absolute;
    top: 12px;
    right: 11px;
    width: 40px;
    height: 40px;
    content: url(../../images/elective.png)
}

2) background-size

.divItem{
    position: absolute;
    top: 12px;
    right: 11px;
    width: 40px;
    height: 40px;
    background-image: url(../../images/elective.png);
    -webkit-background-size: 100%;
    background-size: 100%
}

-webkit-background-size: is specifically for those browsers which support web kit layout rendering engine.
Note : http://en.wikipedia.org/wiki/WebKit






Opera supports 
-o-background-size:
But in latest opera versions, webkit itself is sufficient

3) box-shadow

.divItem{
   border: 1px solid #08c;
   box-shadow: 0 1px 0 rgba(255, 255, 255, 0.25), inset 0 3px 6px rgba(0, 0, 0, 0.25)
 }

This doesn't works in Firefox,

.divItem
{
   border: 1px solid #08c;
  -moz-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.25), inset 0 3px 6px rgba(0, 0, 0, 0.25);
  -webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.25), inset 0 3px 6px rgba(0, 0, 0, 0.25);
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.25), inset 0 3px 6px rgba(0, 0, 0, 0.25)
 }


4) border-radius:

.divItem{
    border-radius: 100px;
}

For Firefox and other browsers
.divItem{
    -moz-border-radius: 100px;
    -webkit-border-radius: 100px;
    border-radius: 100px;
}


5) innerText :
innerText is old style of using in older version of internet explorer.
Instead of using innerText , use textContent or innerHTML, this works fine in all browsers.

User innerHTML Or contentText