Skip to content

Commit

Permalink
[examples] Add Server Rendering implementation (mui#11880)
Browse files Browse the repository at this point in the history
  • Loading branch information
oliviertassinari authored Jun 16, 2018
1 parent 8bc7d11 commit 33a03a0
Show file tree
Hide file tree
Showing 10 changed files with 272 additions and 7 deletions.
25 changes: 19 additions & 6 deletions docs/src/pages/guides/server-rendering/server-rendering.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ import React from 'react';
import App from './App';

// We are going to fill these out in the sections to follow.
function handleRender(req, res) {
function renderFullPage(html, css) {
/* ... */
}

function renderFullPage(html, preloadedState) {
function handleRender(req, res) {
/* ... */
}

Expand Down Expand Up @@ -67,8 +67,13 @@ We then get the CSS from our `sheetsRegistry` using `sheetsRegistry.toString()`.
import { renderToString } from 'react-dom/server'
import { SheetsRegistry } from 'react-jss/lib/jss';
import JssProvider from 'react-jss/lib/JssProvider';
import { MuiThemeProvider, createMuiTheme, createGenerateClassName } from '@material-ui/core/styles';
import { green, red } from '@material-ui/core/colors';
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';

function handleRender(req, res) {
// Create a sheetsRegistry instance.
Expand Down Expand Up @@ -134,7 +139,8 @@ Let's take a look at our client file:
import React from 'react';
import { hydrate } from 'react-dom';
import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles';
import { green, red } from '@material-ui/core/colors';
import green from '@material-ui/core/colors/green';
import red from '@material-ui/core/colors/red';
import App from './App';

class Main extends React.Component {
Expand All @@ -147,7 +153,7 @@ class Main extends React.Component {
}

render() {
return <App {...this.props} />
return <App />
}
}

Expand All @@ -168,6 +174,13 @@ hydrate(
);
```

## Reference implementations

We host different reference implementations which you can find in the [GitHub repository](https://github.com/mui-org/material-ui) under the [`/examples`](https://github.com/mui-org/material-ui/tree/master/examples) folder:
- [The reference implementation of this tutorial](https://github.com/mui-org/material-ui/tree/master/examples/ssr)
- [Next.js](https://github.com/mui-org/material-ui/tree/master/examples/nextjs)
- [Gatsby](https://github.com/mui-org/material-ui/tree/master/examples/gatsby)

## Troubleshooting

If it doesn't work, in 99% of cases it's a configuration issue.
Expand Down
1 change: 0 additions & 1 deletion examples/parcel/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
"babel-plugin-transform-class-properties": "latest",
"babel-preset-env": "latest",
"babel-preset-react": "latest",
"babel-preset-stage-2": "latest",
"parcel-bundler": "latest",
"prop-types": "latest",
"react": "latest",
Expand Down
4 changes: 4 additions & 0 deletions examples/ssr/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"presets": ["@babel/preset-env", "@babel/preset-react"],
"plugins": ["@babel/plugin-proposal-class-properties"]
}
7 changes: 7 additions & 0 deletions examples/ssr/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# dependencies
/node_modules

# production
/build

yarn.lock
71 changes: 71 additions & 0 deletions examples/ssr/App.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React from 'react';
import PropTypes from 'prop-types';
import Button from '@material-ui/core/Button';
import Dialog from '@material-ui/core/Dialog';
import DialogTitle from '@material-ui/core/DialogTitle';
import DialogContent from '@material-ui/core/DialogContent';
import DialogContentText from '@material-ui/core/DialogContentText';
import DialogActions from '@material-ui/core/DialogActions';
import Typography from '@material-ui/core/Typography';
import { withStyles } from '@material-ui/core/styles';

const styles = theme => ({
root: {
textAlign: 'center',
paddingTop: theme.spacing.unit * 20,
},
});

class App extends React.Component {
state = {
open: false,
};

handleClose = () => {
this.setState({
open: false,
});
};

handleClick = () => {
this.setState({
open: true,
});
};

render() {
const { classes } = this.props;
const { open } = this.state;

return (
<div className={classes.root}>
<Dialog open={open} onClose={this.handleClose}>
<DialogTitle>Super Secret Password</DialogTitle>
<DialogContent>
<DialogContentText>1-2-3-4-5</DialogContentText>
</DialogContent>
<DialogActions>
<Button color="primary" onClick={this.handleClose}>
OK
</Button>
</DialogActions>
</Dialog>
<Typography variant="display1" gutterBottom>
Material-UI
</Typography>
<Typography variant="subheading" gutterBottom>
example project
</Typography>
<Button variant="contained" color="secondary" onClick={this.handleClick}>
Super Secret Password
</Button>
</div>
);
}
}

App.propTypes = {
classes: PropTypes.object.isRequired,
};

export default withStyles(styles)(App);
21 changes: 21 additions & 0 deletions examples/ssr/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Server Rendering

## How to use

Download the example [or clone the repo](https://github.com/mui-org/material-ui):

```bash
curl https://codeload.github.com/mui-org/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/ssr
cd ssr
```

Install it and run:

```bash
npm install
npm run start
```

## The idea behind the example

This is the reference implementation of the [Server Rendering tutorial](https://material-ui.com/guides/server-rendering/).
36 changes: 36 additions & 0 deletions examples/ssr/client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from 'react';
import { hydrate } from 'react-dom';
import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles';
import green from '@material-ui/core/colors/green';
import red from '@material-ui/core/colors/red';
import App from './App';

class Main extends React.Component {
// Remove the server-side injected CSS.
componentDidMount() {
const jssStyles = document.getElementById('jss-server-side');
if (jssStyles && jssStyles.parentNode) {
jssStyles.parentNode.removeChild(jssStyles);
}
}

render() {
return <App />;
}
}

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

hydrate(
<MuiThemeProvider theme={theme}>
<Main />
</MuiThemeProvider>,
document.querySelector('#root'),
);
25 changes: 25 additions & 0 deletions examples/ssr/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"name": "ssr",
"version": "1.0.0",
"private": true,
"dependencies": {
"@babel/core": "latest",
"@babel/node": "latest",
"@babel/plugin-proposal-class-properties": "latest",
"@babel/preset-env": "latest",
"@babel/preset-react": "latest",
"@material-ui/core": "latest",
"babel-loader": "next",
"express": "latest",
"nodemon": "latest",
"prop-types": "latest",
"react": "latest",
"react-dom": "latest",
"react-jss": "latest",
"webpack": "latest",
"webpack-cli": "latest"
},
"scripts": {
"start": "webpack -w --mode=development & nodemon --exec yarn babel-node -- server.js"
}
}
70 changes: 70 additions & 0 deletions examples/ssr/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import express from 'express';
import React from 'react';
import { renderToString } from 'react-dom/server';
import { SheetsRegistry } from 'react-jss/lib/jss';
import JssProvider from 'react-jss/lib/JssProvider';
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 App from './App';

function renderFullPage(html, css) {
return `
<!doctype html>
<html>
<head>
<title>Material-UI</title>
</head>
<body>
<script async src="build/bundle.js"></script>
<div id="root">${html}</div>
<style id="jss-server-side">${css}</style>
</body>
</html>
`;
}

function handleRender(req, res) {
// Create a sheetsRegistry instance.
const sheetsRegistry = new SheetsRegistry();

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

const generateClassName = createGenerateClassName();

// Render the component to a string.
const html = renderToString(
<JssProvider registry={sheetsRegistry} generateClassName={generateClassName}>
<MuiThemeProvider theme={theme} sheetsManager={new Map()}>
<App />
</MuiThemeProvider>
</JssProvider>,
);

// Grab the CSS from our sheetsRegistry.
const css = sheetsRegistry.toString();

// Send the rendered page back to the client.
res.send(renderFullPage(html, css));
}

const app = express();

app.use('/build', express.static('build'));

// This is fired every time the server side receives a request.
app.use(handleRender);

const port = 3000;
app.listen(port);
19 changes: 19 additions & 0 deletions examples/ssr/webpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const path = require('path');

module.exports = {
entry: './client.js',
output: {
path: path.resolve(__dirname, 'build'),
filename: 'bundle.js',
publicPath: '/',
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
},
],
},
};

0 comments on commit 33a03a0

Please sign in to comment.