How to implement Server Side Rendering for Material UI's media queries in NextJs to avoid flash?
Tags: javascript, nextjs, mui, react
Published: 2022-10-29 18:02:04

## What is this post about?
This post will help you to implement server side rendering(SSR) for Material UI's media queries if you are using NextJs with MUI(formerly Material-UI). If you implement useMediaQuery using SSR you will avoid the flash of desktop view in the mobile width devices during initial second of the render when you load a page. This post will explain each step to you to make it much more clear.

## Why do you see the flash of desktop view during initial render in mobile ?
Since useMediaQuery Defaults to false. In order to perform the server-side rendering reconciliation, it needs to render twice. A first time with nothing and a second time with the children. This double pass rendering cycle comes with a drawback. If your code contains lines something like below then majorly you will see the flash

```
const xs = useMediaQuery(theme.breakpoints.down('sm'))
...
return(
  {xs ?
     <p>Small device</p>
  :
     <p>Regular size device</p>
  }
)
```

## More context of flash

- If you load the page in mobile, initial second during render will return "Regular size device" and then to "small device" but the case it should render is "Small device" during initial render itself.

- This happens due to not using correct method of useMediaQuery during ssR

Here is the [useMediaquery mui](https://mui.com/material-ui/react-use-media-query/#server-side-rendering)  documentation but its really confusing how to implement in your code, so below are the steps

## Step 1: Get device type from server side App.getInitialProps

- I assume your _app.js looks like below,
```
// _app.js
import NextApp from 'next/app';
import { createTheme } from '@mui/material';


const App = ({ Component, pageProps }) => {

  const theme = createTheme({
    // your MUI theme configuration goes here
    components: {
      //your default component styling goes here
    }
  });

  return (
    <ThemeProvider theme={theme}>
      <Component {...pageProps} />
    </ThemeProvider>
  ); 
};

App.getInitialProps = async (context) => {
  return {
    ...NextApp.getInitialProps(context),
  };
};

export default App;
```

- Edit App.getInitialProps to get deviceType, for this you require [ua-parser-js](https://www.npmjs.com/package/ua-parser-js). install this package and Parse the user agent string of the client to extract device type

```
App.getInitialProps = async (context) => {
  let deviceType;

  if (context.ctx.req) {
    deviceType = parser(context.ctx.req.headers['user-agent']).device.type || 'desktop';
  }

  return {
    ...NextApp.getInitialProps(context),
    deviceType,
  };
};
```

## Step 2: Provide an implementation of ssr matchMedia

- Pass deviceType as prop and the following code in the function
- The recommended way for emulating match media is Using [css-mediaquery](https://github.com/ericf/css-mediaquery)

```
const App = ({ Component, pageProps, deviceType }) => {
  const ssrMatchMedia = (query) => ({
    matches: mediaQuery.match(query, {
      // The estimated CSS width of the browser.
      width: deviceType === 'mobile' ? '0px' : '1024px',
    }),
  });
 ........
```
## Step 3: Override MuiUseMediaQuery default props in theme

- The useMediaQuery is set to false as default. In order to detect the device width in server side you need to change the default props as the ssrMatchMedia defined above.So Add a following lines of code 

```
  const theme = createTheme({
    // your MUI theme configuration goes here
    components: {
      MuiUseMediaQuery: {
        defaultProps: {
          ssrMatchMedia,
        },
      },
    }
```

- This will set the deviceType during server side rendering and apply the mediaQuery before loading the page.

## so final code looks something like below and you can use useMediaQuery in any component to avoid the flash

```
// _app.js
import NextApp from 'next/app';
import parser from 'ua-parser-js';
import mediaQuery from 'css-mediaquery';

import { createTheme } from '@mui/material';


const App = ({ Component, pageProps, deviceType }) => {
  const ssrMatchMedia = (query) => ({
    matches: mediaQuery.match(query, {
      // The estimated CSS width of the browser.
      width: deviceType === 'mobile' ? '0px' : '1024px',
    }),
  });

  const theme = createTheme({
    // your MUI theme configuration goes here
    components: {
      MuiUseMediaQuery: {
        defaultProps: {
          ssrMatchMedia,
        },
      },
    }
  });


  return (
    <ThemeProvider theme={theme}>
      <Component {...pageProps} />
    </ThemeProvider>
  ); 
};

App.getInitialProps = async (context) => {
  let deviceType;

  if (context.ctx.req) {
    deviceType = parser(context.ctx.req.headers['user-agent']).device.type || 'desktop';
  }

  return {
    ...NextApp.getInitialProps(context),
    deviceType,
  };
};

export default App;

```

## Conclusion
If you follow the above steps you will avoid the flash and the case explained in context of flash works smooth.

That's it guys thank you :)