diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..0578679 --- /dev/null +++ b/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["react", "es2015"] +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c860569 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules +built diff --git a/README.md b/README.md new file mode 100644 index 0000000..23891c3 --- /dev/null +++ b/README.md @@ -0,0 +1,92 @@ +# Interactive Guide to Server-side rendering with Webpack, React, React Transmit, CSS modules and more + +Follow the tutorial commit-by-commit, to see the server-side rendering drama unfold with a happy ending! + +[CLICK TO GET STARTED](https://github.com/dimaip/server-side-rendering/commits/master) + + +## Contents (mostly for Google) + +### Step 1: minimal Webpack, Babel and React setup + +RUN: `npm run start` and then open index.html in the browser. + +Webpack helps us to bundle our code with dependencies from npm +(such as React), and then transforms the code with Babel, to make +it compatible with ES5. + +### Step 2: trivial server-side rendering with Express + +RUN: `npm run start` and go to `http://localhost:3000`. + +Now we are rendering the same Hello component both on client and server: +with express server we pre-render the Hello component on the server, and +server the client with rendered html, and with webpack we continue to +bundle client.js into ES5 code that the browser would understand, +just as we did at previous step. + +### Step 3: Add styles + +Now lets learn to deal with styles. We configure webpack loaders to +support loading CSS files. This is cool, but there comes one problem +with server-side rendering: styles won't be loaded until all of JS loads, +so no styles for devices without JS. + +Let's fix this problem with webpack's ExtractTextPlugin plugin: it +extracts all CSS styles into one CSS file that we can serve to our client, +so our page will instantly look perfectly styled, even without JS. + +### Step 3a: switch to CSS modules + +Everybody loves CSS modules, and the great news is that they come free with Webpack. +The bad news is that we can't use them with server-side rendering, as we don't use +Webpack when rendering on the server-side. + +So at this step we broke everything, and the only way to continue from here, is to +start using Webpack to pre-build code for server-side rendering too, and that's +what we'll do at the next step. + +### Step 3b: save the day by making webpack to render server-side code + +To save our issue with CSS modules, we make Webpack to render both +our client and our server side code. The best way to do it is to +use Webpack's abillity to handle array of configs. + +With first config we transform our client-side code (`client.js`), +just as we were doing before. But with the second config we transform +`handleRender` function that we have extracted into `serverEntry.js`, +so now our server-side rendering code gets processed by Webpack too. +There we use css-loader/locals as a CSS loader, to just get class names from +CSS modules, as that's all we need to render html on the server. +Also notice how we use `target:node` and `nodeExternals`. + +Great! Now our build is fixed, so we can use CSS modules both during client +and server rendering. + + +### Step 4a: asyncronously fetching data + +Now let's fetch some data asyncronously. We'll use isomorphic-fetch, +as it works both on client and server our of the box. + +Fetching data works just fine on the server, but the problem is that +on the server we didn't wait for fetch to finish fetching the data +before sending the response, so our pre-rendered html doesn't have any +async data when it arrives. +Let's try to fix it in the next step. + + +### Step 4b: use react-transmit to declaratively define data deps + +There are multiple ways to solve async rendering issue. Here we'll +use react-transmit to declaratively define our data dependencies per component, +and return rendered html only when all data is resolved. + +It works even with nested components, constructing the single promises tree, +which is qute cool. It is inspired by Facebook Relay, so if you are familiar +with it, you'll feel right at home. + + +## That's all, folks! + +Got more tips or challenges with server-side rendering of React? Submit a PR! diff --git a/index.html b/index.html new file mode 100644 index 0000000..ff81169 --- /dev/null +++ b/index.html @@ -0,0 +1,10 @@ + + + + Server-side rendering tutorial + + +
+ + + diff --git a/index.js b/index.js new file mode 100644 index 0000000..a9ec4a9 --- /dev/null +++ b/index.js @@ -0,0 +1,13 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; + +var Hello = React.createClass({ + render: function() { + return
Hello {this.props.name}
; + } +}); + +ReactDOM.render( + , + document.getElementById('app') +); diff --git a/package.json b/package.json new file mode 100644 index 0000000..3fd22b3 --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "name": "server-side-rendering", + "version": "0.0.1", + "description": "The missing guide to server-side rendering", + "main": "index.js", + "scripts": { + "start": "webpack" + }, + "author": "Dmitri Pisarev", + "license": "ISC", + "dependencies": { + "path": "^0.12.7", + "react": "^0.14.8", + "react-dom": "^0.14.8" + }, + "devDependencies": { + "webpack": "^1.12.14", + "babel-cli": "^6.6.5", + "babel-loader": "^6.2.4", + "babel-preset-es2015": "^6.6.0", + "babel-preset-react": "^6.5.0" + } +} diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 0000000..9834311 --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,25 @@ +const path = require('path'); +const webpack = require('webpack'); + +const config = { + entry: ['index.js'], + output: { + path: path.join(__dirname, 'built/'), + filename: 'index.js', + publicPath: '/built/' + }, + module: { + loaders: [ + { + test: /\.js$/, + exclude: /node_modules/, + loader: 'babel' + } + ] + }, + resolve: { + root: __dirname + } +}; + +module.exports = config;