Skip to content

Commit

Permalink
[docs] Improve useMediaQuery SSR docs
Browse files Browse the repository at this point in the history
  • Loading branch information
oliviertassinari committed Sep 15, 2019
1 parent 6e27604 commit ed138fb
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 31 deletions.
10 changes: 1 addition & 9 deletions docs/src/pages/components/use-media-query/ServerSide.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,8 @@ function MyComponent() {

export default function ServerSide() {
const ssrMatchMedia = query => ({
// Use https://github.com/ericf/css-mediaquery as ponyfill.
matches: mediaQuery.match(query, {
// The estimated CSS width of the browser.
// For the sake of this demo, we are using a fixed value.
//
// In production, you can leverage:
//
// - Client hints. You can ask the client to send your server its width.
// Be aware that this feature is not supported everywhere: https://caniuse.com/#search=client%20hint.
// - User-agent. You can parse the user agent of the client, then convert the data to a
// is mobile or is desktop variable, and finally, guess the most likely screen width of the client.
width: 800,
}),
});
Expand All @@ -30,6 +21,7 @@ export default function ServerSide() {
<ThemeProvider
theme={{
props: {
// Change the default options of useMediaQuery
MuiUseMediaQuery: { ssrMatchMedia },
},
}}
Expand Down
85 changes: 63 additions & 22 deletions docs/src/pages/components/use-media-query/use-media-query.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,26 +58,9 @@ You can use [json2mq](https://github.com/akiran/json2mq) to generate media query

{{"demo": "pages/components/use-media-query/JavaScriptMedia.js", "defaultCodeOpen": true}}

## Server-side rendering

An implementation of [matchMedia](https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia) is required on the server.
Using [css-mediaquery](https://github.com/ericf/css-mediaquery) to emulate it is recommended.

{{"demo": "pages/components/use-media-query/ServerSide.js"}}

⚠️ Server-side rendering and client-side media queries are fundamentally at odds.
Be aware of the tradeoff. The support can only be partial.

Try relying on client-side CSS media queries first.
For instance, you could use:

- [`<Box display>`](/system/display/#hiding-elements)
- [`<Hidden implementation="css">`](/components/hidden/#css)
- or [`themes.breakpoints.up(x)`](/customization/breakpoints/#css-media-queries)

## Testing

Similar to the server-side case, you need an implementation of [matchMedia](https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia) in your test environment.
You need an implementation of [matchMedia](https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia) in your test environment.

For instance, [jsdom doesn't support it yet](https://github.com/jsdom/jsdom/blob/master/test/web-platform-tests/to-upstream/html/browsers/the-window-object/window-properties-dont-upstream.html). You should polyfill it.
Using [css-mediaquery](https://github.com/ericf/css-mediaquery) to emulate it is recommended.
Expand All @@ -100,6 +83,65 @@ describe('MyTests', () => {
});
```

## Server-side rendering

> ⚠️ Server-side rendering and client-side media queries are fundamentally at odds.
Be aware of the tradeoff. The support can only be partial.

Try relying on client-side CSS media queries first.
For instance, you could use:

- [`<Box display>`](/system/display/#hiding-elements)
- [`themes.breakpoints.up(x)`](/customization/breakpoints/#css-media-queries)
- or [`<Hidden implementation="css">`](/components/hidden/#css)

If none of the above alternatives are an option, you can proceed reading this section of the documentation.

First, you need to guess the characteristics of the client request, from the server.
You have the choice between using:

- **User agent**. Parse the user agent string of the client to extract information. Using [ua-parser-js](https://github.com/faisalman/ua-parser-js) to parse the user agent is recommended.
- **Client hints**. Read the hints the client is sending to the server. Be aware that this feature is [not supported everywhere](https://caniuse.com/#search=client%20hint).

Finally, you need to provide an implementation of [matchMedia](https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia) to the `useMediaQuery` with the previously guessed characteristics.
Using [css-mediaquery](https://github.com/ericf/css-mediaquery) to emulate matchMedia is recommended.

For instance:

```js
import ReactDOMServer from 'react-dom/server';
import parser from 'ua-parser-js';
import mediaQuery from 'css-mediaquery';
import { ThemeProvider } from '@material-ui/styles';

function handleRender(req, res) {
const deviceType = parser(req.headers['user-agent']).device.type || 'desktop';
const ssrMatchMedia = query => ({
matches: mediaQuery.match(query, {
// The estimated CSS width of the browser.
width: deviceType === 'mobile' ? 0 : 1024,
}),
});

const html = ReactDOMServer.renderToString(
<ThemeProvider
theme={{
props: {
// Change the default options of useMediaQuery
MuiUseMediaQuery: { ssrMatchMedia },
},
}}
>
<App />
</ThemeProvider>,
);

//
}
```

{{"demo": "pages/components/use-media-query/ServerSide.js"}}

## Migrating from `withWidth()`

The `withWidth()` higher-order component injects the screen width of the page.
Expand All @@ -123,10 +165,9 @@ You can reproduce the same behavior with a `useWidth` hook:
A first time with nothing and a second time with the children.
This double pass rendering cycle comes with a drawback. It's slower.
You can set this flag to `true` if you are **not doing server-side rendering**.
- `options.ssrMatchMedia` (*Function* [optional]) You might want to use an heuristic to approximate
the screen of the client browser.
For instance, you could be using the user-agent or the client-hint https://caniuse.com/#search=client%20hint.
You can provide a global ponyfill using [`custom props`](/customization/globals/#default-props) on the theme. Check the [server-side rendering example](#server-side-rendering).
- `options.ssrMatchMedia` (*Function* [optional]) You can provide your own implementation of *matchMedia*. This especially useful for [server-side rendering support](#server-side-rendering).

Note: You can change the default options using the [`default props`](/customization/globals/#default-props) feature of the theme with the `MuiUseMediaQuery` key.

#### Returns

Expand Down

0 comments on commit ed138fb

Please sign in to comment.